2010-11-18 11 views
10

¿Cómo escribiría una función en C que hace una comparación atómica y se intercambia en un valor entero, utilizando código de máquina incrustado (suponiendo, por ejemplo, arquitectura x86)? ¿Puede ser más específico si está escrito solo para el procesador i7?Compare e intercambie el código de máquina en C

¿Funciona la traducción como una valla de memoria, o solo asegura la relación de ordenamiento solo en esa ubicación de memoria incluida en la comparación y el intercambio? ¿Qué tan costoso es comparado con una valla de memoria?

Gracias.

+0

Normalmente se compara y se intercambia un valor con una ubicación de memoria, parece que se habla de _dos ubicaciones de memoria que es más compleja. ¿Es esto definitivamente lo que necesitas? –

+0

Lo siento, me refería a una sola ubicación de memoria, lo aclararé. – axel22

Respuesta

7

La manera más fácil de hacerlo es probablemente con compiler intrinsic como _InterlockedCompareExchange(). Parece una función, pero en realidad es un caso especial en el compilador que se reduce a una sola operación de máquina. En el caso del MSVC x86 intrínseco, también funciona como una valla de lectura/escritura, pero eso no es necesariamente cierto en otras plataformas. (Por ejemplo, en el PowerPC, necesitaría emitir explícitamente un lwsync para validar el reordenamiento de la memoria.)

En general, en muchos sistemas comunes, una operación de comparación y cambio generalmente solo impone una transacción atómica sobre el una dirección que está tocando. Se puede reordenar el acceso a otra memoria, y en los sistemas multinúcleo, las direcciones de memoria que no sean la que ha intercambiado pueden no ser coherentes entre los núcleos.

+0

¡Gracias por tu respuesta! ¿Pero es incluso posible hacer una comparación y valla sin valla de memoria en cualquiera de los sistemas multinúcleo actuales? En esta pregunta, http://stackoverflow.com/questions/4183202/java-compare-and-swap-semantics-and-performance, un usuario afirmó que en las arquitecturas x86, la única instrucción de comparación e intercambio es CMPXCHG, y que tiene que protegerse a través de LOCK, que actúa como una valla de memoria, para hacerlo atómico, que esta es la única forma. ¿Sabes quizás si este reclamo es correcto? – axel22

+1

Creo que para el x86 tiene razón (no soy un experto en Intel). Pero hay otros procesadores con otra semántica. Por ejemplo, PowerPC tiene un modelo diferente donde crea una "reserva" en una dirección y luego almacena de manera condicional. Pero esto solo garantiza una valla en esa ubicación. Un almacén anterior en una ubicación diferente por otro núcleo podría aparecer después del compare-and-swap. Además, en ese chip, "la coherencia no garantiza que el resultado de una tienda por un procesador sea visible inmediatamente para todos los demás procesadores ". – Crashworks

+1

Es decir: supongamos que hay dos núcleos, A y B. Si A y luego B hacen C y S en la dirección 0x100, se pondrán de acuerdo sobre el orden en que esto sucede. B verá el primer valor de A y luego B. Pero, si A hace una escritura ordinaria de "0" para direccionar 0x100, entonces B escribe "1" a 0x100, y luego ambos C y S en la dirección 0x200 - luego ambos verán el mismo valor en 0x200, pero A todavía puede piensa que 0x100 contiene "0". De hecho, la escritura de A podría llegar a 0x100 después de B, de modo que el valor realmente termina siendo 0. – Crashworks

7

Puede usar la instrucción CMPXCHG con el prefijo LOCK para la ejecución atómica.

E.g.

lock cmpxchg DWORD PTR [ebx], edx 

o

lock cmpxchgl %edx, (%ebx) 

Esto se compara el valor en el registro EAX con el valor en la dirección almacenada en el registro EBX y almacena el valor en el registro EDX a ese lugar si son los mismos , de lo contrario, carga el valor en la dirección almacenada en el registro EBX en EAX.

Necesita tener un 486 o posterior para que esta instrucción esté disponible.

+1

Charles, ¿esto se puede usar? sin el BLOQUEO, si se puede garantizar que solo un hilo lo usará? –

+0

@Guru del equipo: Sí, se puede usar sin 'BLOQUEO '. –

+0

Gracias por confirmar que :) –

4

Si su valor entero es de 64 bit, utilice cmpxchg8b 8 byte compare e intercambie bajo IA32 x86. La variable debe estar alineada con 8 bytes.

Example: 
     mov eax, OldDataA   //load Old first 32 bits 
     mov edx, OldDataB   //load Old second 32 bits 
     mov ebx, NewDataA   //load first 32 bits 
     mov ecx, NewDataB   //load second 32 bits 
     mov edi, Destination  //load destination pointer 
     lock cmpxchg8b qword ptr [edi] 
     setz al      //if transfer is succesful the al is 1 else 0 
+1

Nunca carga' edx' y 'eax 'que es el par de valores que se compara. –

+0

Tiene razón a haber reparado mi código. –

3

Si el prefijo LOCK se omite en las instrucciones del procesador atómicas, se no puede afectar al funcionamiento atómica a través de entorno multiprocesador.

En un entorno multiprocesador, la señal LOCK # asegura que el procesador tiene uso exclusivo de cualquier memoria compartida mientras se afirma la señal. Intel Instruction Set Reference

Sin el prefijo LOCK, la operación garantizará que no se interrumpa por ningún evento (interrupción) en el procesador/núcleo actual solamente.

2

Es interesante observar que algunos procesadores no proporcionan un intercambio de comparación, sino que proporcionan algunas otras instrucciones ("Load Linked" y "Conditional Store") que se pueden usar para sintetizar la comparación desafortunadamente llamada-y -swap (el nombre suena como debería ser similar a "compare-exchange" pero realmente debería llamarse "compare-and-store" ya que hace la comparación, almacena si el valor coincide, e indica si el valor coincide y la tienda se realizó). Las instrucciones no pueden sintetizar la semántica de cambio de comparación (que proporciona el valor leído en caso de que falle la comparación), pero en algunos casos puede evitar el problema de ABA presente en Compare-Exchange. Muchos algoritmos se describen en términos de operaciones "CAS" porque se pueden usar en ambos estilos de CPU.

Una instrucción "Load Linked" le dice al procesador que lea una ubicación en la memoria y mire de alguna manera para ver si se puede escribir. Una instrucción de "Almacenamiento condicional" instruye al procesador para que escriba una ubicación de memoria solo si nada puede haberla escrito desde la última operación de "Cargar Vinculado". Tenga en cuenta que la determinación puede ser pesimista; el procesamiento de una interrupción, por ejemplo, puede invalidar una secuencia "Vinculada a la carga"/"Almacén condicional". Del mismo modo, en un sistema multiprocesador, una secuencia de LL/CS puede ser invalidada por otra CPU que accede a una ubicación en la misma línea de caché que la ubicación que se está viendo, incluso si no se tocó la ubicación real que se está mirando. En el uso típico, LL/CS se utilizan muy juntos, con un ciclo de reintento, por lo que las invalidaciones erróneas pueden desacelerar un poco pero no causarán muchos problemas.

Cuestiones relacionadas