2011-08-04 2 views
13

Je suis nouveau à l'utilisation de gcc inline assembly, et je me demandais si, sur une machine multicœur x86, un spinlock (sans conditions de course) pourrait être implémenté comme (en utilisant la syntaxe T):x86 spinlock en utilisant cmpxchg

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

spin_unlock: 
lock mov 0 [lock_addr] 
ret 

Répondre

21

Vous avez la bonne idée, mais votre asm est cassé:

cmpxchg peut ne pas fonctionner avec un opérande immédiat, seuls les registres.

lock n'est pas un préfixe valide pour mov. mov à une adresse alignée est atomique sur x86, de sorte que vous n'avez pas besoin de lock de toute façon.

Il a été un certain temps depuis que je suis utilisé à & T syntaxe, l'espoir je me suis souvenu tout:

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 

Notez que GCC a builtins atomique, de sorte que vous n'avez pas réellement besoin d'utiliser asm en ligne à cette fin:

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; 
} 

Comme Bo dit ci-dessous, les instructions verrouillées encourent un coût: tous que vous utilisez doit vider votre cache et verrouiller le bus mémoire de votre système, ce qui peut être assez cher si vous avez suffisamment de processeurs. Même sans beaucoup de processeurs, il est toujours facile et vaut la peine d'optimiser autour de:

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

L'instruction pause est vital pour les performances sur les processeurs HyperThreading lorsque vous avez du code qui tourne comme ça - il permet au second fil exécuter tandis que le premier fil tourne. Sur les processeurs qui ne prennent pas en charge pause, il est traité comme nop.

+0

Si le paramètre pour spin_lock void() être déclaré aussi volatile? – ManRow

+1

N ° '__sync_bool_compare_and_swap' le traite déjà comme' volatile'. –

+0

L'asm utilisé comme barrière de mémoire à l'intérieur de 'spin_unlock' devrait probablement inclure le clobber de la mémoire. Cependant, d'un autre côté, il y a '__sync_lock_release' qui est conçu pour faire la chose" write barrier, and write 0 "sans avoir à penser à asm du tout, et il est même" quelque peu portable ". Il ne fonctionne pas explicitement en tant que barrière de lecture (il le fait _incidentially_ sur l'architecture cible), mais c'est OK. La pire chose à faire est qu'un autre thread fait un tour supplémentaire dans un cas rare et improbable. – Damon

3

Cela mettra moins contention sur le bus mémoire:

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

D'accord, même si ce code n'est pas très bon. Un simple while (* p) peut facilement être optimisé par le compilateur. Ajoutez quelques barrières. En outre, l'ajout de _mm_pause() pour les puces Intel peut considérablement améliorer les performances. –

Questions connexes