2011-08-04 17 views
13

Sono nuovo nell'utilizzo dell'assembly in gcc e mi chiedevo se, su una macchina multi-core x86, uno spinlock (senza condizioni di competizione) potesse essere implementato come (usando la sintassi AT &):x86 spinlock utilizzando cmpxchg

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

spin_unlock: 
lock mov 0 [lock_addr] 
ret 

risposta

21

hai l'idea giusta, ma il vostro asm è rotto:

cmpxchg non può funzionare con un operando immediato, registra solo.

lock non è un prefisso valido per mov. mov a un indirizzo allineato è atomico su x86, quindi non è necessario lo lock.

E 'stato un po' di tempo da quando ho usato AT & sintassi T, spero ricordava tutto:

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 

Nota che GCC ha builtins atomiche, quindi in realtà non è necessario utilizzare asm inline per raggiungere questo obiettivo:

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

Come Bo dice qui di seguito, le istruzioni bloccati sostenere un costo: ognuno si utilizza deve svuotare la cache e bloccare bus di memoria del sistema, che può essere molto costoso se hai abbastanza CPU. Anche senza molte CPU, è ancora facile e vale la pena di ottimizzare in giro:

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

L'istruzione pause è di vitale importanza per le prestazioni su CPU HyperThreading quando hai il codice che gira in questo modo - permette il secondo thread di esecuzione mentre il primo thread sta girando. Nelle CPU che non supportano pause, viene considerato come nop.

+0

caso il parametro per spin_lock void() anche essere dichiarato volatili? – ManRow

+1

No. '__sync_bool_compare_and_swap' lo considera già come 'volatile'. –

+0

L'asm usato come barriera di memoria all'interno di 'spin_unlock' dovrebbe probabilmente includere il clobber di memoria. D'altra parte, c'è '__sync_lock_release' che è progettato solo per fare la" barriera di scrittura e scrivere 0 "cosa senza dover pensare affatto a asm, ed è persino" un po 'portatile ". Non funziona esplicitamente come barriera di lettura (lo fa incidentalmente_ sull'architettura di destinazione), ma va bene. La cosa peggiore da fare è un altro thread che fa un singolo giro in più in un caso raro e improbabile. – Damon

3

Questo metterà meno contesa sul bus di memoria:

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

D'accordo, anche se questo codice non è così buono. Un semplice while (* p) può essere facilmente ottimizzato dal compilatore. Aggiungi alcune barriere. Inoltre, l'aggiunta di _mm_pause() per i chip Intel può migliorare significativamente le prestazioni. –