2009-09-24 16 views
9

J'ai fait un spinlock très simple en utilisant les fonctions Interlocked dans Windows et l'ai testé sur un CPU dual-core (deux threads qui incrémentent une variable);Intel Inspector signale une course de données dans mon implémentation de spinlock

Le programme semble fonctionner OK (il donne le même résultat à chaque fois, ce qui est le cas en l'absence de synchronisation est utilisé), mais Intel Parallel Inspector dit qu'il ya une condition de course à valeur + = j (voir le code ci-dessous). L'avertissement disparaît lorsque vous utilisez des sections critiques au lieu de SpinLock.

Mon implémentation de SpinLock est-elle correcte ou non? C'est vraiment étrange, parce que toutes les opérations utilisées sont atomiques et ont les barrières de mémoire appropriées et cela ne devrait pas conduire à des conditions de course.

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

Le programme d'essai:

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

Répondre

4

documentation de l'inspecteur en parallèle pour data race suggère d'utiliser une section critique ou d'un mutex réparer les courses sur Windows. Il n'y a rien dans ce qui suggère que Parallel Inspector sait reconnaître tout autre mécanisme de verrouillage que vous pourriez inventer.

Les outils d'analyse de nouveaux mécanismes de verrouillage ont tendance à être des outils statiques qui examinent chaque chemin possible à travers le code, la documentation de Parallel Inspector implique qu'il exécute le code une seule fois.

Si vous voulez expérimenter de nouveaux mécanismes de verrouillage, l'outil le plus commun que j'ai vu utilisé dans la littérature académique est le Spin model checker. Il y a aussi ESP, ce qui pourrait réduire l'espace d'état, mais je ne sais pas si cela a été appliqué à des problèmes concurrents, et aussi le mobility workbench qui donnerait une analyse si vous pouviez résoudre votre problème en pi-calcul. Intel Parallel Inspector ne semble pas aussi compliqué que ces outils, mais plutôt conçu pour vérifier les problèmes courants en utilisant des heuristiques.

1

Je suis sûr qu'il devrait être mis en œuvre comme suit:

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

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

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

Ce que vous proposez est de la même chose avec ce que je fais, seulement que l'int autour les spins de verrouillage sont contenus dans la classe ... et ce serait un inconvénient, car RAII ne peut plus être utilisé (la classe a aussi un destructeur qui libère automatiquement le verrou). J'ai pensé, j'ai essayé ce que vous avez dit, et c'est la même chose: le programme fonctionne correctement, mais Intel Parallel Inspector dit qu'il y a une condition de concurrence. Peut-être que l'inspecteur a un bug? Mais probablement pas :( –

+0

vous devriez également utiliser long pour commencer au lieu de faire la distribution explicite, et dans le constructeur ne prennent pas de paramètres et commencent juste à le déverrouiller.Si la chose qui le crée a besoin d'être verrouillé pour commencer, ils peuvent juste verrouillez-le après l'avoir créé mais avant de le partager. @Igratian - Vous n'avez pas besoin de RAII dans ce cas puisque le destructeur n'a rien à nettoyer (c'est juste un long) –

+0

Edited.Ça ne dérange pas d'ajouter le déconstructeur ... car la question ne justifie pas de corriger totalement son code. J'essayais juste de résoudre son problème. – Goz

2

Pour les autres pauvres dans une situation similaire à moi: Intel fournit un ensemble d'inclusions et de bibliothèques pour faire exactement ce genre de chose. Vérifiez dans le répertoire d'installation d'Inspector (vous trouverez \ include, \ lib32 et \ lib64 dans le répertoire d'installation) pour ces matériaux. Documentation sur la façon de les utiliser (en décembre 2014):

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

Il y a 3 fonctions:

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

Je l'ai testé avec mon propre mutex spin-lock et cela fonctionne comme prévu (les données disparaissent du rapport de Parallel Inspector) – Julio

+0

De rien. – johnwbyrd