Question

À quelle fréquence vous vous trouvez réellement utiliser spinlocks dans votre code? Comment est-il fréquent de rencontrer une situation où l'aide d'une boucle d'attente surclasse en fait l'utilisation de serrures?
Personnellement, quand j'écris une sorte de code qui exige la sécurité des threads, je tends à référence avec différentes primitives de synchronisation, et dans la mesure où il va, il semble que l'aide de serrures donne de meilleures performances que l'utilisation spinlocks. Peu importe combien peu de temps, je tiens effectivement le verrou, le montant de discorde que je reçois lors de l'utilisation spinlocks est beaucoup plus grande que le montant que je reçois de l'aide des serrures (bien sûr, je lance mes tests sur une machine multiprocesseur).

Je me rends compte qu'il est plus susceptible de rencontrer un spinlock dans le code « bas niveau », mais je suis intéressé de savoir si vous trouvez qu'il est utile même dans un genre plus de programmation de haut niveau?

Était-ce utile?

La solution

Cela dépend de ce que vous faites. Dans le code d'application générale, vous voulez éviter spinlocks.

Dans des choses de bas niveau où vous ne le verrou est posé pour un couple d'instructions, et la latence est importante, un tapis de spinlock être une meilleure solution qu'un verrou. Mais ces cas sont rares, surtout dans le genre d'applications où C # est généralement utilisé.

Autres conseils

En C #, « spin locks » ont été, dans mon expérience, presque toujours pire que de prendre un verrou -. Il est un événement rare où les verrous de rotation surpasseront un verrou

Cependant, ce n'est pas toujours le cas. .NET 4 est l'ajout d'un System.Threading Structure .SpinLock . Cela offre des avantages dans des situations où une serrure est maintenue pendant un temps très court, et accaparés à plusieurs reprises. A partir de la documentation MSDN sur Structures de données pour la programmation parallèle :

  

Dans les scénarios où l'attente de la serrure devrait être courte, SpinLock offre de meilleures performances que d'autres formes de verrouillage.

circlips peuvent surpasser les autres mécanismes de verrouillage dans les cas où vous faites quelque chose comme le verrouillage par un arbre - si vous êtes seulement d'avoir des verrous sur chaque nœud pour une très, très peu de temps, ils peuvent en effectuer une traditionnelle fermer à clé. Je suis tombé sur ceci dans un moteur de rendu avec une mise à jour de la scène multithread, à un moment donné -. Circlips sur profilés à surperformer verrouillage avec Monitor.Enter

Pour mon travail en temps réel, en particulier avec les pilotes de périphériques, je les ai utilisé un peu juste. Il se trouve que (quand dernière je l'ai chronométré cela) en attente d'un objet de synchronisation comme un sémaphore lié à un matériel d'interruption mâche au moins 20 microsecondes, peu importe combien de temps il faut en fait pour l'interruption de se produire. Un seul contrôle d'un registre matériel mappé en mémoire, suivi d'un chèque à RDTSC (pour permettre un temps afin que vous ne bloquez pas la machine) est dans le haut de gamme de nannosecond (basiquement dans le bruit). Pour handshaking niveau du matériel qui ne devrait pas prendre beaucoup de temps, il est vraiment difficile de battre un spinlock.

Mon 2c: Si vos mises à jour répondent à certains critères d'accès alors ils sont de bons candidats spinlock:

  • rapide , à savoir que vous aurez le temps d'acquérir le spinlock, effectuer les mises à jour et relâchez le verrou tournant dans un seul fil quanta de sorte que vous ne soyez pas préempté tout en maintenant le spinlock
  • localisée toutes les données à jour sont de préférence dans une seule page qui est déjà chargé, vous ne voulez pas TLB pendant que vous tenant le spinlock, et vous definetely ne veux pas un swap de défaillance de page lire!
  • atomique vous n'avez pas besoin un autre verrou pour effectuer l'opération, à savoir. jamais attendre pour les serrures sous spinlock.

Pour tout ce qui a tout le potentiel pour produire, vous devez utiliser une structure de verrouillage (notification des événements, mutex, sémaphores, etc.).

Un cas d'utilisation pour les serrures de rotation est si vous vous attendez affirmation très bas, mais allez avoir beaucoup d'entre eux. Si vous n'avez pas besoin pour le verrouillage récursive support, un spinlock peut être mis en œuvre dans un seul octet, et si affirmation est très faible, les déchets de cycle CPU est négligeable.

Pour un cas d'utilisation pratique, j'ai souvent des réseaux de milliers d'éléments, où les mises à jour à différents éléments du tableau peuvent se produire en toute sécurité en parallèle. Les chances de deux fils essayant de mettre à jour en même temps le même élément sont très petites (faible contention) mais je besoin d'un verrou pour chaque élément (je vais avoir beaucoup d'entre eux). Dans ces cas, je prévoient généralement un tableau de ubytes de la même taille que le tableau que je suis mise à jour en parallèle et mettent en place spinlocks en ligne comme (dans le langage de programmation D):

while(!atomicCasUbyte(spinLocks[i], 0, 1)) {}
    myArray[i] = newVal;
atomicSetUbyte(spinLocks[i], 0);

Par contre, si je devais utiliser des serrures régulières, je dois allouer un tableau de pointeurs vers des objets, puis allouer un objet Mutex pour chaque élément de ce tableau. Dans les scénarios tels que celui décrit ci-dessus, cela est tout simplement un gaspillage ordinaire.

Si vous avez la performance code critique et vous avez déterminé qu'il doit être plus rapide qu'elle ne l'est actuellement et vous avez déterminé que le facteur critique est la vitesse de verrouillage, alors ce serait une bonne idée d'essayer un spinlock. Dans d'autres cas, pourquoi la peine? serrures normales sont plus faciles à utiliser correctement.

S'il vous plaît noter les points suivants:

  1. La plupart des implémentations de mutexe tournent un peu de temps avant que le fil est en fait non planifié. En raison de cela, il est difficile de comparer les thèses mutex avec spinlocks pures.

  2. Plusieurs discussions SPINING « aussi vite que possible » sur le même spinlock se consomé toute la bande passante et de réduire drastiquement l'efficacité de votre programme. Vous devez ajouter petit « dormir » du temps en ajoutant noop dans votre boucle de repoussage.

Vous presque jamais besoin d'utiliser spinlocks dans le code d'application, si tout ce que vous devez les éviter.

Je ne peux pas chose aucune raison d'utiliser un spinlock en c # code en cours d'exécution sur un système d'exploitation normal. serrures occupés sont la plupart du temps une perte au niveau de l'application - le filage peut vous amener à utiliser l'ensemble timeslice cpu, contre une serrure entraînera immédiatement un changement de contexte si nécessaire.

Code de haute performance où vous avez nr de fils = nr de processeurs / coeurs pourraient bénéficier dans certains cas, mais si vous avez besoin d'optimisation des performances à ce niveau votre rendant probable jeu next gen 3D, travailler sur un système d'exploitation embarqué avec des primitives de synchronisation pauvres , la création d'un système d'exploitation / pilote ou en tout cas ne pas utiliser c #.

J'ai utilisé des serrures de rotation pour la phase d'arrêt du monde du garbage collector dans mon HLVM projet parce qu'ils sont faciles et qui est une machine virtuelle de jouet. Cependant, les verrous de rotation peuvent être contre-productif dans ce contexte:

L'un des bugs dans le perf garbage collector du compilateur Glasgow Haskell est si ennuyeux qu'il a un nom, le " dernier ralentissement noyau ». Ceci est une conséquence directe de leur utilisation inappropriée de spinlocks dans leur GC et excacerbated sur Linux en raison de son planificateur, mais, en fait, l'effet peut être observé lorsque d'autres programmes sont en compétition pour le temps CPU.

L'effet est clair sur le deuxième graphique ici et peut être vu affecter plus que le dernier noyau ici , où le programme Haskell voit une dégradation des performances au-delà de seulement 5 noyaux.

Gardez toujours ces points dans votre esprit tout en utilisant spinlocks :

  • exécution rapide en mode utilisateur.
  • Synchronise threads dans un processus unique, ou plusieurs processus si dans la mémoire partagée.
  • Ne renvoie jusqu'à ce que l'objet appartient.
  • Ne supporte pas récursivité.
  • Consomme 100% du CPU en "attente".

Je l'ai personnellement vu tant de blocages juste parce que quelqu'un pensait que ce sera une bonne idée d'utiliser spinlock.

Soyez très très prudent lors de l'utilisation spinlocks

(Je ne peux pas souligner assez).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top