Может кто-нибудь сказать мне, является ли std :: atomic :: is_lock_free () не статичным, а также constexpr? Не иметь смысла в том, чтобы он был нестатичным и / или неконстекстовым.
Может кто-нибудь сказать мне, является ли std :: atomic :: is_lock_free () не статичным, а также constexpr? Не иметь смысла в том, чтобы он был нестатичным и / или неконстекстовым.
Ответы:
Как объяснено на cppreference :
Все атомарные типы, за исключением std :: atomic_flag, могут быть реализованы с использованием мьютексов или других операций блокировки, а не с помощью инструкций атомарного процессора без блокировки. Атомарные типы также могут быть иногда свободными от блокировки, например, если только выровненные обращения к памяти естественным образом атомарны в данной архитектуре, неправильно выровненные объекты одного типа должны использовать блокировки.
Стандарт C ++ рекомендует (но не требует), чтобы атомарные операции без блокировки также были безадресными, то есть подходящими для связи между процессами, использующими разделяемую память.
Как уже упоминалось несколькими другими, std::is_always_lock_freeможет быть то, что вы действительно ищете.
Редактировать: Чтобы уточнить, типы объектов C ++ имеют значение выравнивания, которое ограничивает адреса их экземпляров только определенными кратными степенями two ( [basic.align]). Эти значения выравнивания определяются реализацией для основных типов и не должны равняться размеру типа. Они также могут быть более строгими, чем то, что на самом деле может поддерживать оборудование.
Например, x86 (в основном) поддерживает невыровненный доступ. Тем не менее, вы найдете большинство компиляторов alignof(double) == sizeof(double) == 8для x86, поскольку невыровненный доступ имеет множество недостатков (скорость, кэширование, атомарность ...). Но, например, #pragma pack(1) struct X { char a; double b; };или alignas(1) double x;позволяет вам иметь «не выровненные» doubleс. Поэтому, когда cppreference говорит о «выровненных обращениях к памяти», он, по-видимому, делает это с точки зрения естественного выравнивания типа для аппаратного обеспечения, не используя тип C ++ таким образом, который противоречит его требованиям к выравниванию (которое будет UB).
Вот дополнительная информация: Каков реальный эффект от успешного доступа без выравнивания на x86?
Пожалуйста, ознакомьтесь с проницательными комментариями @Peter Cordes ниже!
alignof(double)==4. Но std::atomic<double>все равно alignof() = 8вместо проверки выравнивания во время выполнения. Использование упакованной структуры, которая выравнивает атомарные нити, нарушает ABI и не поддерживается. (GCC для 32-битных x86 предпочитает естественное выравнивание 8-байтовых объектов, но правила упаковки структуры переопределяют это и основаны только на них alignof(T), например, на i386 System V. В G ++ раньше была ошибка, когда atomic<int64_t>внутри структуры не было бы атомарной потому что это только предполагалось. GCC (для C не C ++) все еще имеет эту ошибку!)
std::atomic_ref<double>либо полностью отклонит заниженное выравнивание double, либо проверит выравнивание во время выполнения на платформах, где это допустимо для простого doubleи int64_tбудет меньше, чем естественное выравнивание. (Потому что atomic_ref<T>действует на объект, который был объявлен как простой T, и имеет только минимальное выравнивание alignof(T)без возможности дать ему дополнительное выравнивание.)
_Atomic int64_tпри компиляции с током gcc -m32. В любом случае, я хочу сказать, что настоящие компиляторы не поддерживают недооцененные атомики и не выполняют проверки во время выполнения (пока?), Поэтому #pragma packили __attribute__((packed))просто приведут к неатомарности; объекты все равно сообщат, что они есть lock_free.
is_lock_free()состоит в том, чтобы позволить реализациям работать не так, как на самом деле; с проверками времени выполнения, основанными на фактическом выравнивании, для использования атомарных инструкций, поддерживаемых HW, или для использования блокировки.
Вы можете использовать std::is_always_lock_free
is_lock_free зависит от конкретной системы и не может быть определена во время компиляции.
Соответствующее объяснение:
Атомарные типы также могут иногда быть свободными от блокировки, например, если только выровненные обращения к памяти естественным образом атомарны в данной архитектуре, неправильно выровненные объекты одного типа должны использовать блокировки.
std::numeric_limits<int>::maxзависит от архитектуры, но является статичным и constexpr. Я думаю, в этом нет ничего плохого, но я не покупаю первую часть рассуждений
is_lock_free смысла .
Я установил Visual Studio 2019 на свой Windows-ПК, и у этого устройства также есть ARMv8-компилятор. ARMv8 допускает не выровненный доступ, но сравнение и обмен, заблокированные добавления и т. Д. Должны быть выровнены. Кроме того, чистая загрузка / чистое хранение с использованием ldpили stp(пара загрузки или пара хранения 32-разрядных регистров) гарантированно будут атомарными, только если они естественным образом выровнены.
Поэтому я написал небольшую программу для проверки того, что is_lock_free () возвращает для произвольного атомного указателя. Итак, вот код:
#include <atomic>
#include <cstddef>
using namespace std;
bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
return a64->is_lock_free();
}
И это разборка isLockFreeAtomic
|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
movs r0,#1
bx lr
ENDP
Это просто returns true, ака 1.
Эта реализация выбирает для использования, alignof( atomic<int64_t> ) == 8так что каждый atomic<int64_t>правильно выровнен. Это исключает необходимость проверки выравнивания во время выполнения при каждой загрузке и хранении.
(Примечание редактора: это часто встречается; большинство реальных реализаций C ++ работают именно так. Вот почему std::is_always_lock_freeэто так полезно: потому что это обычно верно для типов, где is_lock_free()всегда верно.)
atomic<uint64_t>и alignof() == 8поэтому им не нужно проверять выравнивание во время выполнения. Этот старый API дает им возможность не делать этого, но на текущем HW имеет гораздо больше смысла просто требовать выравнивания (в противном случае UB, например, не атомарность). Даже в 32-битном коде, где int64_tможет быть только 4-байтовое выравнивание, atomic<int64_t>требуется 8-байтовый. Смотрите мои комментарии к другому ответу
alignofзначение для базового типа таким же, как «хорошее» выравнивание аппаратного обеспечения, то is_lock_free всегда будет true(и так будет is_always_lock_free). Ваш компилятор здесь делает именно это. Но API существует, поэтому другие компиляторы могут делать разные вещи.
alignof(std::atomic<double>) == 1(так что не было бы никакого «неприровненного доступа» в смысле C ++, следовательно, нет UB), даже если аппаратное обеспечение может гарантировать только атомарные операции без блокировки для doubles на 4 или 8-байтовые границы. Затем компилятору придется использовать блокировки в невыровненных случаях (и возвращать соответствующее логическое значение из is_lock_free, в зависимости от расположения в памяти экземпляра объекта).
is_always_lock_free?