2012-05-08 13 views
14

En una máquina x86 multinúcleo, decir que una ejecución de subproceso en core1 incrementa una variable entera a al mismo tiempo, la secuencia en el núcleo 2 también la incrementa. Dado que el valor inicial de a era 0, ¿sería siempre 2 al final? O podría tener algún otro valor? Supongamos que a se declara como volatile y no estamos utilizando variables atómicas (como atomic <> de C++ y operaciones atómicas incorporadas en gcc).¿Se incrementa un número entero atómico en x86?

Si el valor de a De hecho, sería siempre 2, en tal caso, ¿significa que un long int en x86-64 también tendría la misma propiedad, es decir, siempre habrá a 2 en el final?

+4

a menos que esté utilizando un tipo atómico especial, el incremento suele ser tres operaciones separadas. Cargar, Incrementar, luego Almacenar. –

+4

'volátil' no le da acceso atómico. –

+3

@CatPlusPlus es su nombre una operación atómica? : P – MByD

Respuesta

24

La instrucción de máquina de memoria incremental en un X86 es atómica solo si la usa con un prefijo LOCK.

x ++ en C y C++ no tiene comportamiento atómico. Si realiza incrementos desbloqueados, debido a las carreras en las que el procesador lee y escribe X, si dos procesadores independientes intentan un incremento, puede terminar con un solo incremento o con ambos (el segundo procesador puede haber leído el valor inicial, incrementado) y luego lo escribe después de que el primero escribe sus resultados).

Creo que C++ 11 ofrece incrementos atómicos, y la mayoría de los compiladores de proveedores tienen una forma idiomática de causar un incremento atómico de ciertos tipos enteros incorporados (generalmente int y long); mira tu manual de referencia del compilador.

Si desea incrementar un "valor grande" (por ejemplo, un entero de multiprerencia), debe hacerlo utilizando un mecanismo de bloqueo estándar, como un semáforo.

Tenga en cuenta que debe preocuparse por atomic lee, también. En el x86, leer un valor de 32 o 64 bits resulta ser atómico si es una palabra de 64 bits alineada. Eso no será verdad de un "gran valor"; nuevamente necesitarás un candado estándar.

+0

¿El prefijo LOCK también asegura las vallas necesarias? (Creo que sí, pero no estoy seguro.) –

+0

Sí. .......... –

4

No se garantiza. Puede usar la instrucción lock xadd para lograr el mismo efecto, o usar C++ std::atomic, o usar #pragma omp atomic, o cualquier cantidad de otras soluciones de simultaneidad que se hayan escrito para evitar la reinvención de la rueda.

7

He aquí una prueba de que no es atómica en una aplicación en particular (GCC), como se puede ver (?), Gcc genera código que

  1. carga el valor de la memoria a un registro
  2. incrementos el contenido del registro
  3. guarda el registro en la memoria.

Eso está muy lejos de ser atómico.

$ cat t.c 
volatile int a; 

void func(void) 
{ 
    a++; 
} 
[19:51:52 0 ~] $ gcc -O2 -c t.c 
[19:51:55 0 ~] $ objdump -d t.o 

t.o:  file format elf32-i386 


Disassembly of section .text: 

00000000 <func>: 
    0: a1 00 00 00 00   mov 0x0,%eax 
    5: 83 c0 01    add $0x1,%eax 
    8: a3 00 00 00 00   mov %eax,0x0 
    d: c3      ret 

No se deje engañar por el 0x0 en la instrucción mov, hay espacio para 4 bytes allí, y el enlazador se complete la dirección de memoria resultante para a allí cuando este archivo objeto está vinculado.

+0

Es curioso, en realidad solo se obtiene load/add/store cuando 'a' es' volátil', de lo contrario, gcc usa una lectura-modificación-escritura (a menos que se ajuste para [i586 (pentium)] (https://godbolt.org/g/3K8zfu)). Por supuesto, si el código circundante usa el valor de num ++, lo más probable es que se haga con instrucciones separadas para dejar el resultado en un registro. Mencioné esto en mi respuesta sobre [una versión no-volátil] la pregunta] (http://stackoverflow.com/questions/39393850/can-num-be-atomic-for-int-num/39396999#39396999). –

+0

@PaterCordes Sin volátil, gcc podría generar una sola instrucción 'addl', aunque eso tampoco es atómico a menos que también use el prefijo 'lock'. – nos

+0

: P El segundo enlace en mi comentario anterior es mi respuesta que explica exactamente (en términos del protocolo MESI, etc.) por qué 'addl $ 1, num' no es atómico (excepto en un sistema uniprocesador si no lo hacemos) incluir observadores de DMA), y por qué es con 'lock'. –

7

Puesto que nadie ha respondido a su pregunta real y en su lugar están mostrando cómo hacerlo de una manera que siempre funciona:

Tema 1 cargas valor de 0

Tema 2 cargas valor de 0

Thread incrementos de 1 un tiendas 1

Thread 2 incrementos de su copia local de registro de valor y tiendas 1.

Como Puede ver que el resultado final es un valor igual a 1 y no a 2. No siempre será 2 al final.

Cuestiones relacionadas