2009-09-24 7 views
9

Ho creato uno spinlock molto semplice utilizzando le funzioni Interlocked in Windows e testato su una CPU dual-core (due thread che incrementano una variabile);Intel Inspector segnala una corsa di dati nella mia implementazione di spinlock

Il programma sembra funzionare bene (dà lo stesso risultato ogni volta, che non è il caso in cui non viene utilizzato alcun la sincronizzazione), ma Intel Parallel Inspector dice che c'è una condizione di competizione al valore + = j (vedere il codice sotto). L'avviso scompare quando si utilizzano le sezioni critiche anziché il mio SpinLock.

La mia implementazione di SpinLock è corretta o no? È davvero strano, perché tutte le operazioni usate sono atomiche e hanno le appropriate barriere di memoria e non dovrebbero portare a condizioni di gara.

class SpinLock 
{ 
    int *lockValue; 
    SpinLock(int *value) : lockValue(value) { } 

    void Lock() { 
     while(InterlockedCompareExchange((volatile LONG*)lockValue, 1, 0) != 0) { 
      WaitABit(); 
     } 
    } 

    void Unlock() { InterlockedExchange((volatile LONG*)lockValue, 0); } 
}; 

Il programma di test:

static const int THREADS = 2; 
HANDLE completedEvents[THREADS]; 
int value = 0; 
int lock = 0; // Global. 

DWORD WINAPI TestThread(void *param) { 
    HANDLE completed = (HANDLE)param; 
    SpinLock testLock(&lock); 

    for(int i = 0;i < 1000*20; i++) { 
     for(int j = 0;j < 10*10; j++) { 
      // Add something to the variable. 
      testLock.Lock(); 
      value += j; 
      testLock.Unlock(); 
     } 
    } 
    SetEvent(completed); 
} 

int main() { 
    for(int i = 0; i < THREADS; i++) { 
     completedEvents[i] = CreateEvent(NULL, true, false, NULL); 
    } 
    for(int i = 0; i < THREADS; i++) { 
     DWORD id; 
     CreateThread(NULL, 0, TestThread, completedEvents[i], 0, &id); 
    } 

    WaitForMultipleObjects(THREADS, completedEvents, true, INFINITE); 
    cout<<value; 
} 

risposta

4
documentazione

dell'ispettore parallela per data race suggerisce di utilizzare una sezione critica o di un mutex per correggere gare su Windows. Non c'è niente in esso che suggerisca che Parallel Inspector sappia riconoscere qualsiasi altro meccanismo di blocco che potresti inventare.

Gli strumenti per l'analisi di nuovi meccanismi di blocco tendono ad essere strumenti statici che esaminano ogni possibile percorso attraverso il codice, la documentazione di Parallel Inspector implica che esegue il codice una sola volta.

Se si desidera sperimentare nuovi meccanismi di blocco, lo strumento più comune che ho visto utilizzato nella letteratura accademica è lo Spin model checker. C'è anche ESP, che potrebbe ridurre lo spazio degli stati, ma non so se è stato applicato a problemi concomitanti, e anche lo mobility workbench che darebbe un'analisi se è possibile risolvere il problema in pi-calcolo. Intel Parallel Inspector non sembra complicato come questi strumenti, ma piuttosto progettato per verificare problemi ricorrenti ricorrendo all'euristica.

1

Sono abbastanza sicuro che dovrebbe essere attuato nel modo seguente:

class SpinLock 
{ 
    long lockValue; 
    SpinLock(long value) : lockValue(value) { } 

    void Lock() { 
     while(InterlockedCompareExchange(&lockValue, 1, 0) != 0) { 
      WaitABit(); 
     } 
    } 

    void Unlock() { InterlockedExchange(&lockValue, 0); } 
}; 
+0

Quello che vi proposto è di circa lo stesso con quello che faccio, solo che l'int intorno il blocco degli spin è contenuto nella classe ... e questo sarebbe uno svantaggio, perché RAII non può più essere usato (la classe ha anche un distruttore che rilascia automaticamente il blocco). Pensavo, ho provato quello che hai detto, ed è lo stesso: il programma funziona correttamente, ma Intel Parallel Inspector dice che c'è una condizione di competizione. Forse l'ispettore ha un bug? Ma probabilmente no :( –

+0

dovresti anche usare molto tempo per iniziare invece di fare il cast esplicito, e nel costruttore non prendere parametri e solo avviarlo sbloccato.Se la cosa che lo ha creato deve averlo bloccato per cominciare, possono solo bloccalo dopo averlo creato ma prima di condividerlo. @Igratian - In questo caso non hai bisogno di RAII dato che il distruttore non ha nulla da pulire (è solo un lungo). –

+0

Modificato.Non mi preoccuperò di aggiungere il decostruttore ... poiché la domanda non garantisce la totale correzione del suo codice. Stavo solo cercando di risolvere il suo problema. – Goz

2

Per gli altri poveri in una situazione simile a me: Intel fornisce un set di include e librerie per fare esattamente questo genere di cose. Controlla nella directory di installazione di Inspector (vedrai \ include, \ lib32 e \ lib64 nella directory di installazione) per quei materiali. Documentazione su come usarli (al dicembre 2014):

https://software.intel.com/sites/products/documentation/doclib/iss/2013/inspector/lin/ug_docs/index.htm#GUID-E8B4A8F7-45C3-489C-A5E3-1C9CC525BA9C.htm

ci sono 3 funzioni:

void __itt_sync_acquired(void *addr) 
void __itt_sync_releasing(void *addr) 
void __itt_sync_destroy(void *addr) 
+0

Ho provato questo con il mio mutex spin-lock e funziona come previsto (la corsa dei dati scompare dal rapporto di Parallel Inspector) – Julio

+0

Prego. – johnwbyrd

Problemi correlati