2012-03-15 11 views
10

Necesito leer/escribir de forma atómica en 16 bytes. Solo escribo usando cmpxchg16, que está disponible en todos los procesadores x64 excepto en el caso de uno oscuro de AMD.Lectura atómica de 16 bytes en CPU x64

Ahora la pregunta es para los valores alineados de 16 bytes, solo se modifica utilizando cmpxchg16 (que actúa como una barrera de memoria completa) ¿es posible leer una ubicación de 16 bytes que es mitad de datos antiguos y mitad datos nuevos?

Mientras lea con una instrucción SSE (por lo que el hilo no se puede interrumpir en el medio de la lectura) creo que es imposible (incluso en sistemas multiprocesador numa) que la lectura vea datos inconsistentes. Creo que debe ser atómico.

Supongo que cuando se ejecuta cmpxchg16, modifica los 16 bytes atómicamente, no escribiendo dos bloques de 8 bytes con la posibilidad de que otros hilos hagan una lectura intermedia (sinceramente, no veo cómo podría funcionar si no fuera atómico.)

¿Estoy en lo correcto? Si estoy equivocado, ¿hay alguna manera de hacer una lectura atómica de 16 bytes sin recurrir al bloqueo?

Nota: Hay un couple similar questions here pero no se ocupan del caso en que las escrituras solo se realizan con cmpxchg16, por lo que creo que esta es una pregunta separada, sin respuesta.

Edit: En realidad, creo que mi razonamiento era defectuoso. Una instrucción de carga SSE se puede ejecutar como dos lecturas de 64 bits, y es posible que cmpxchg16 se ejecute entre las dos lecturas por otro procesador.

+1

Ya se respondió en la pregunta vinculada que 16 bytes SSE lee se puede implementar con accesos a la memoria múltiple, es decir, que no son atómicos. No hace la diferencia que sus escrituras se hagan atómicamente con CMPXCHG16B. Las lecturas también deben ser atómicas o puede que vea datos inconsistentes. AFAIK su única opción es leer con CMPXCHG16B. – Timo

+0

Sí, cometí el error de pensar que solo tengo que evitar que el hilo se interrumpa entre las lecturas, pero las operaciones reales del bus en sí podrían estar entrelazadas. – Eloff

+0

Usar cmpxchg16b en las lecturas los ralentizaría inaceptablemente. Pero al usar un 25% más de memoria puedo hacer un enfoque de estilo seqlock como el hashmap de Dmitry Vyukov: http://www.1024cores.net/home/downloads – Eloff

Respuesta

9
typedef struct 
{ 
    unsigned __int128 value; 
} __attribute__ ((aligned (16))) atomic_uint128; 

unsigned __int128 
atomic_read_uint128 (atomic_uint128 *src) 
{ 
    unsigned __int128 result; 
    asm volatile ("xor %%rax, %%rax;" 
       "xor %%rbx, %%rbx;" 
       "xor %%rcx, %%rcx;" 
       "xor %%rdx, %%rdx;" 
       "lock cmpxchg16b %1" : "=A"(result) : "m"(*src) : "rbx", "rcx"); 
    return result; 
} 

Eso debería hacer el truco. El typedef asegura la alineación correcta. El cmpxchg16b necesita que los datos se alineen en un límite de 16 bytes.

El cmpxchg16b probará si *src contiene un cero y escribe un cero si es así (nop). En cualquier caso, el valor correcto se mantendrá en RAX: RDX después.

El código anterior se evalúa como algo tan simple como

push %rbx 
xor %rax,%rax 
xor %rbx,%rbx 
xor %rcx,%rcx 
xor %rdx,%rdx 
lock cmpxchg16b (%rdi) 
pop %rbx 
retq 
+0

Sí, creo que esta debe ser la manera de hacerlo.Se me ocurre ahora que una simple carga SSE se puede dividir en dos lecturas de 64 bits y que el cmpxchg16 podría ocurrir entre las lecturas. – Eloff

1

Según referencias http://siyobik.info/main/reference/instruction/CMPXCHG8B%2FCMPXCHG16B la CMPXCHG16 no es por defecto atómico pero se pueden hacer atómica utilizando LOCKhttp://siyobik.info/main/reference/instruction/LOCK

Eso significa que, por defecto, los datos pueden ser modificados dentro de la lectura y escritura fases. Locking hace que tanto la lectura como la escritura sean atómicas.

+0

"Tenga en cuenta que CMPXCHG16B requiere que el operando de destino (memoria) esté alineado 16 bytes " – kay

+0

Lo siento, sí, quise decir cmpxchg16 con el prefijo de bloqueo. Pero el bloqueo no se puede usar con instrucciones SSE. – Eloff

Cuestiones relacionadas