2010-11-18 15 views
10

Come si può scrivere una funzione in C che fa un confronto atomico e di swap su un valore intero, utilizzando codice macchina incorporato (assumendo, per esempio, l'architettura x86)? Può essere più specifico se è scritto solo per il processore i7?Confronto e scambio in codice macchina in C

agisce il traduzione come un recinto di memoria, o ha semplicemente assicurare ordinare relazione proprio su quella posizione di memoria inclusa nella confrontare e scambiare? Quanto è costoso rispetto ad una recinzione di memoria?

Grazie.

+0

In genere si confronta e si scambia un valore con una posizione di memoria, sembra che si parli di due luoghi di memoria più complessi. E 'sicuramente quello di cui hai bisogno? –

+0

Scusa, ho inteso una singola posizione di memoria, chiarirò. – axel22

risposta

7

Il modo più semplice per farlo è probabilmente con un compiler intrinsic come _InterlockedCompareExchange(). Sembra una funzione, ma in realtà è un caso speciale nel compilatore che si riduce a un singolo computer operativo. Nel caso di MSVC x86 intrinseco, funziona anche come recinzione di lettura/scrittura, ma questo non è necessariamente vero su altre piattaforme. (Ad esempio, sul PowerPC, è necessario emettere esplicitamente un lwsync per bloccare il riordino della memoria.)

In generale, su molti sistemi comuni, un'operazione di confronto e scambio di solito applica solo una transazione atomica sul un indirizzo è commovente. È possibile riordinare altri accessi alla memoria e, nei sistemi multicore, gli indirizzi di memoria diversi da quelli scambiati potrebbero non essere coerenti tra i core.

+0

Grazie per la risposta! Ma è persino possibile fare un confronto-e-scambio non-memory-fence su uno qualsiasi dei sistemi multicore attuali? In questa domanda http://stackoverflow.com/questions/4183202/java-compare-and-swap-semantics-and-performance, un utente ha affermato che su architetture x86, l'unica istruzione di confronto e scambio è CMPXCHG e che deve essere protetto tramite LOCK, che funge da recinto di memoria, per renderlo atomico - che questo è l'unico modo. Sai forse se questa affermazione è corretta? – axel22

+1

Penso che per l'x86 abbia ragione (non sono un esperto di Intel). Ma ci sono altri processori con altre semantiche. Ad esempio, il PowerPC ha un modello diverso in cui crea una "prenotazione" su un indirizzo e quindi memorizza condizionatamente. Ma questo garantisce solo un recinto su quella posizione. Potrebbe apparire un deposito precedente in un'altra posizione di un altro core dopo il confronto e lo swap. Inoltre, su quel chip, "la coerenza non garantisce che il risultato di un negozio da parte di un processore sia immediatamente visibile a tutti gli altri processori ". – Crashworks

+1

Vale a dire: ci sono due core, A e B. Se A e B fanno entrambi C & S sull'indirizzo 0x100, saranno d'accordo sull'ordine in cui ciò accade. B vedrà il primo valore A e poi B's. Ma, se A fa una scrittura ordinaria di "0" per indirizzare 0x100, allora B scrive "1" a 0x100, e quindi entrambi C & S sull'indirizzo 0x200 - in seguito entrambi vedranno lo stesso valore a 0x200, ma A potrebbe ancora pensa che 0x100 contenga "0". In effetti, la scrittura di A potrebbe arrivare a 0x100 dopo B, in modo che il valore finisca per essere 0. – Crashworks

7

È possibile utilizzare l'istruzione CMPXCHG con il prefisso LOCK per l'esecuzione atomica.

E.g.

lock cmpxchg DWORD PTR [ebx], edx 

o

lock cmpxchgl %edx, (%ebx) 

Questo confronta il valore nella EAX registri con il valore all'indirizzo memorizzato nel registro EBX e memorizza il valore nel EDX registrati per quella posizione se sono la stessa , altrimenti carica il valore all'indirizzo memorizzato nel registro EBX in EAX.

È necessario disporre di un 486 o successivo affinché questa istruzione sia disponibile.

+1

Charles, può essere usato senza il LOCK se si può garantire che solo un singolo thread lo userà? –

+0

@Computer Guru: Sì, può essere usato senza 'LOCK'. –

+0

Grazie per aver confermato che :) –

4

Se il valore intero è 64 bit rispetto a cmpxchg8b 8 byte confrontare e scambiare sotto IA32 x86. La variabile deve essere allineata a 8 byte.

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

Non si carica mai' edx' e 'eax 'che è la coppia di valori che viene confrontata –

+0

Hai ragione a aver riparato il mio codice. –

3

Se il prefisso BLOCCO viene omesso nelle istruzioni del processore atomici, il funzionamento di tutti atomica ambiente multiprocessore saranno non essere garantita.

In un ambiente multiprocessore, il segnale LOCK # assicura che il processore abbia l'uso esclusivo di qualsiasi memoria condivisa mentre il segnale è asserito. Intel Instruction Set Reference

Senza prefisso LOCK l'operazione garantisce che non venga interrotto da alcun evento (interrupt) solo sul processore/core corrente.

2

È interessante notare che alcuni processori non forniscono un confronto-scambio, ma forniscono invece altre istruzioni ("Load Linked" e "Conditional Store") che possono essere utilizzati per sintetizzare il nome sfortunatamente confrontato e -swap (il nome suona come dovrebbe essere simile a "compare-exchange", ma dovrebbe essere chiamato "compare-and-store" poiché fa il confronto, memorizza se il valore corrisponde e indica se il valore corrisponde e lo store è stata eseguita). Le istruzioni non possono sintetizzare la semantica di confronto-scambio (che fornisce il valore letto nel caso in cui il confronto non sia riuscito), ma in alcuni casi può evitare il problema ABA che è presente con Compare-Exchange. Molti algoritmi sono descritti in termini di operazioni "CAS" perché possono essere utilizzati su entrambi gli stili di CPU.

Un'istruzione "Load Linked" indica al processore di leggere una posizione di memoria e guardare in qualche modo per vedere se potrebbe essere scritta. Un'istruzione "Conditional Store" indica al processore di scrivere una posizione di memoria solo se nulla può averlo scritto dall'ultima operazione "Load Linked". Si noti che la determinazione può essere pessimista; l'elaborazione di un interrupt, ad esempio, può invalidare una sequenza "Load-Linked"/"Conditional Store". Allo stesso modo in un sistema multiprocessore, una sequenza LL/CS può essere invalidata da un'altra CPU che accede a una posizione sulla stessa linea della cache della posizione che si sta guardando, anche se la posizione effettiva che si sta guardando non è stata toccata. Nell'uso tipico, LL/CS vengono utilizzati molto vicini tra loro, con un ciclo di tentativi, in modo che invalidi errati possano rallentare un po 'le cose, ma non causano molti problemi.