2011-08-04 17 views
13

Soy nuevo en el uso del ensamblado en línea gcc, y me preguntaba si, en una máquina multinúcleo x86, podría implementarse un spinlock (sin condiciones de carrera) como (usando AT & T sintaxis):x86 spinlock usando cmpxchg

 
spin_lock: 
mov 0 eax 
lock cmpxchg 1 [lock_addr] 
jnz spin_lock 
ret 

spin_unlock: 
lock mov 0 [lock_addr] 
ret 

Respuesta

21

Usted tiene la idea correcta, pero su asm fue quebrado;

cmpxchg no se puede trabajar con un operando inmediato, sólo se registra.

lock no es un prefijo válido para mov. mov a una dirección alineada es atómica en x86, por lo que no necesita lock de todos modos.

Ha pasado algún tiempo desde que he utilizado a & sintaxis T, espero que recordaba todo:

spin_lock: 
xorl %ecx, %ecx 
incl %ecx 
spin_lock_retry: 
xorl %eax, %eax 
lock; cmpxchgl %ecx, (lock_addr) 
jnz spin_lock_retry 
ret 

spin_unlock: 
movl $0 (lock_addr) 
ret 

Tenga en cuenta que GCC tiene órdenes internas atómicas, por lo que en realidad no necesita utilizar asm en línea a lograr esto:

void spin_lock(int *p) 
{ 
    while(!__sync_bool_compare_and_swap(p, 0, 1)); 
} 

void spin_unlock(int volatile *p) 
{ 
    asm volatile (""); // acts as a memory barrier. 
    *p = 0; 
} 

Como dice Bo continuación, las instrucciones cerradas incurren en un costo: cada uno que utilice debe vaciar la caché y de cierre del bus de memoria del sistema, que puede ser bastante caro si tienes suficientes CPU. Incluso sin muchas CPU, sigue siendo fácil y vale la pena para optimizar todo:

void spin_lock(int volatile *p) 
{ 
    while(!__sync_bool_compare_and_swap(p, 0, 1)) 
    { 
     while(*p) _mm_pause(); 
    } 
} 

La instrucción pause es vital para el rendimiento en CPU HyperThreading cuando se tiene código que gira como esto - que permite que el segundo hilo de ejecución mientras el primer hilo está girando. En las CPU que no admiten pause, se trata como nop.

+0

caso de que el parámetro para spin_lock vacío() también se declarará volátil? – ManRow

+1

No. '__sync_bool_compare_and_swap' ya lo trata como' volátil'. –

+0

El asm utilizado como barrera de memoria dentro de 'spin_unlock' probablemente debería incluir memory clobber. Aunque, por otro lado, hay '__sync_lock_release' que está diseñado para hacer la función" escribir barrera y escribir 0 "sin necesidad de pensar en asm, e incluso es" algo portátil ". No funciona explícitamente como barrera de lectura (lo hace _incidentially_ en la arquitectura de destino), pero está bien. Lo peor que puede pasar es otro hilo que hace un solo giro adicional en un caso raro e improbable. – Damon

3

Esto pondrá menos contención en el bus de memoria:

void spin_lock(int *p) 
{ 
    while(!__sync_bool_compare_and_swap(p, 0, 1)) while(*p); 
} 
+0

De acuerdo, aunque este código no es tan bueno. Un tiempo simple (* p) puede ser optimizado fácilmente por el compilador. Agrega algunas barreras. Además, agregar _mm_pause() para los chips Intel puede mejorar significativamente el rendimiento. –