¿Alguien puede decirme si std :: atomic :: is_lock_free () no es estático y constexpr? Tenerlo no estático y / o como no constexpr no tiene sentido para mí.
¿Alguien puede decirme si std :: atomic :: is_lock_free () no es estático y constexpr? Tenerlo no estático y / o como no constexpr no tiene sentido para mí.
Respuestas:
Como se explica en cppreference :
Todos los tipos atómicos, excepto std :: atomic_flag, pueden implementarse utilizando mutexes u otras operaciones de bloqueo, en lugar de utilizar las instrucciones de la CPU atómica sin bloqueo. A veces, los tipos atómicos también pueden estar libres de bloqueo, por ejemplo, si solo los accesos de memoria alineados son naturalmente atómicos en una arquitectura dada, los objetos desalineados del mismo tipo tienen que usar bloqueos.
El estándar C ++ recomienda (pero no requiere) que las operaciones atómicas sin bloqueo también estén libres de dirección, es decir, adecuadas para la comunicación entre procesos que utilizan memoria compartida.
Como lo mencionaron muchos otros, std::is_always_lock_free
podría ser lo que realmente estás buscando.
Editar: para aclarar, los tipos de objetos C ++ tienen un valor de alineación que restringe las direcciones de sus instancias a solo ciertos múltiplos de potencias de dos ( [basic.align]
). Estos valores de alineación están definidos por la implementación para los tipos fundamentales y no necesitan ser iguales al tamaño del tipo. También pueden ser más estrictos de lo que el hardware podría soportar.
Por ejemplo, x86 (principalmente) admite accesos no alineados. Sin embargo, encontrará que la mayoría de los compiladores tienen alignof(double) == sizeof(double) == 8
para x86, ya que los accesos no alineados tienen una serie de desventajas (velocidad, almacenamiento en caché, atomicidad ...). Pero, por ejemplo, #pragma pack(1) struct X { char a; double b; };
o le alignas(1) double x;
permite tener "no alineados" double
. Entonces, cuando cppreference habla de "accesos de memoria alineados", presumiblemente lo hace en términos de la alineación natural del tipo para el hardware, no utilizando un tipo C ++ de una manera que contradiga sus requisitos de alineación (que sería UB).
Aquí hay más información: ¿Cuál es el efecto real de los accesos no alineados exitosos en x86?
¡Por favor también mire los comentarios perspicaces de @Peter Cordes a continuación!
alignof(double)==4
. Pero std::atomic<double>
todavía tiene en alignof() = 8
lugar de verificar la alineación en tiempo de ejecución. El uso de una estructura empaquetada que subalinea atómica rompe el ABI y no es compatible. (GCC para x86 de 32 bits prefiere dar una alineación natural a los objetos de 8 bytes, pero las reglas de empaquetamiento de estructuras anulan eso y solo se basan en alignof(T)
, por ejemplo, en el Sistema i386 V. G ++ solía tener un error donde atomic<int64_t>
dentro de una estructura podría no ser atómico porque simplemente asumió. ¡GCC (para C no C ++) todavía tiene este error!)
std::atomic_ref<double>
rechazará por double
completo la falta de alineación, o verificará la alineación en tiempo de ejecución en plataformas donde es legal por simple double
y int64_t
estar menos alineado de forma natural. (Debido a que atomic_ref<T>
opera en un objeto que se declaró como plano T
, y solo tiene una alineación mínima alignof(T)
sin la oportunidad de darle una alineación adicional.)
_Atomic int64_t
cuando se compila con la corriente gcc -m32
. De todos modos, mi punto es que reales compiladores no son compatibles con las atómicas bajo-alineados, y no hacen los controles de tiempo de ejecución (¿todavía?), Así que #pragma pack
o __attribute__((packed))
se acaba de conducir a la no atomicidad; los objetos aún informarán que lo son lock_free
.
is_lock_free()
es permitir que las implementaciones funcionen de manera diferente a como funcionan las actuales; con comprobaciones de tiempo de ejecución basadas en la alineación real para usar instrucciones atómicas compatibles con HW o para usar un bloqueo.
Puedes utilizar std::is_always_lock_free
is_lock_free
depende del sistema real y no se puede determinar en tiempo de compilación.
Explicación relevante:
Los tipos atómicos también pueden estar libres de bloqueo, por ejemplo, si solo los accesos de memoria alineados son naturalmente atómicos en una arquitectura dada, los objetos desalineados del mismo tipo tienen que usar bloqueos.
std::numeric_limits<int>::max
depende de la arquitectura, pero es estática y constexpr
. Supongo que no hay nada malo en la respuesta, pero no compro la primera parte del razonamiento
is_lock_free
tiene sentido en ese compilador .
Instalé Visual Studio 2019 en mi PC con Windows y este dispositivo también tiene un compilador ARMv8. ARMv8 permite accesos no alineados, pero la comparación y los intercambios, las adiciones bloqueadas, etc. están obligados a alinearse. Y también la carga pura / almacenamiento puro usando ldp
o stp
(pares de carga o pares de almacenamiento de registros de 32 bits) solo se garantiza que sean atómicos cuando están naturalmente alineados.
Entonces escribí un pequeño programa para verificar qué devuelve is_lock_free () para un puntero atómico arbitrario. Así que aquí está el código:
#include <atomic>
#include <cstddef>
using namespace std;
bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
return a64->is_lock_free();
}
Y este es el desmontaje de isLockFreeAtomic
|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
movs r0,#1
bx lr
ENDP
Esto es solo returns true
, alias 1
.
Esta implementación elige usar alignof( atomic<int64_t> ) == 8
para que todos atomic<int64_t>
estén correctamente alineados. Esto evita la necesidad de verificaciones de alineación de tiempo de ejecución en cada carga y almacén.
(Nota del editor: esto es común; la mayoría de las implementaciones de C ++ en la vida real funcionan de esta manera. Por eso std::is_always_lock_free
es tan útil: porque generalmente es cierto para tipos donde is_lock_free()
siempre es cierto).
atomic<uint64_t>
y alignof() == 8
para que no tenga que comprobar el alineamiento en tiempo de ejecución. Esta antigua API les da la opción de no hacerlo, pero en HW actual tiene mucho más sentido solo requerir alineación (de lo contrario, UB, por ejemplo, no atomicidad). Incluso en el código de 32 bits donde int64_t
solo podría tener una alineación de 4 bytes, atomic<int64_t>
requiere 8 bytes. Vea mis comentarios sobre otra respuesta
alignof
valor para un tipo fundamental de la misma como la "buena" la alineación del hardware, entonces is_lock_free
será siempre true
(y lo hará is_always_lock_free
). Su compilador aquí hace exactamente esto. Pero la API existe para que otros compiladores puedan hacer cosas diferentes.
alignof(std::atomic<double>) == 1
(por lo que no habría "acceso no alineado" en el sentido de C ++, por lo tanto, no UB), incluso si el hardware solo puede garantizar operaciones atómicas sin bloqueo para double
s en 4 o Límites de 8 bytes. El compilador tendría que usar bloqueos en los casos no alineados (y devolver el valor booleano apropiado de is_lock_free
, dependiendo de la ubicación de la memoria de la instancia del objeto).
is_always_lock_free
?