Quel est le coût de l'opération atomique (n'importe laquelle des opérations de comparaison et d'échange ou d'ajout / décrémentation atomique)? Combien de cycles consomme-t-il? Interrompra-t-il d'autres processeurs sur SMP ou NUMA, ou bloquera-t-il les accès à la mémoire? Videra-t-il le tampon de réorganisation dans le processeur en panne?
Quels seront les effets sur le cache?
Je m'intéresse aux processeurs modernes et populaires: x86, x86_64, PowerPC, SPARC, Itanium.
Réponses:
J'ai recherché des données réelles ces derniers jours et je n'ai rien trouvé. Cependant, j'ai fait des recherches, qui comparent le coût des opérations atomiques avec les coûts des échecs de cache.
Le coût du préfixe x86 LOCK, (y compris
lock cmpxchg
pour atomic CAS), avant PentiumPro (comme décrit dans la doc), est un accès mémoire (comme un cache manquant), + l'arrêt des opérations mémoire par d'autres processeurs, + tout conflit avec d'autres processeurs essayant de VERROUILLER le bus. Cependant, depuis PentiumPro, pour la mémoire cache en réécriture normale (toute la mémoire traitée par une application, à moins que vous ne parliez directement avec le matériel), au lieu de bloquer toutes les opérations de mémoire, seule la ligne de cache appropriée est bloquée (en fonction du lien dans la réponse de @ osgx ) .c'est-à-dire que le cœur retarde la réponse aux demandes de partage MESI et de RFO pour la ligne jusqu'à la fin de la partie stockage de l'
lock
opération d'édition réelle . Cela s'appelle un "verrou de cache" et n'affecte qu'une seule ligne de cache. D'autres cœurs peuvent charger / stocker ou même CASing d'autres lignes en même temps.En fait, le cas CAS peut être plus compliqué, comme expliqué sur cette page , sans timing mais avec une description perspicace par un ingénieur de confiance. (Au moins pour le cas d'utilisation normal où vous effectuez un chargement pur avant le CAS réel.)
Avant d'entrer dans trop de détails, je dirai qu'une opération VERROUILLÉE coûte un manque de cache + le conflit possible avec un autre processeur sur la même ligne de cache, tandis que CAS + la charge précédente (qui est presque toujours requise sauf sur les mutex, où vous CAS 0 et 1) peuvent coûter deux erreurs de cache.
Il explique qu'un chargement + CAS sur un seul emplacement peut en fait coûter deux échecs de cache, comme Load-Linked / Store-Conditional (voir ici pour ce dernier). Son explication repose sur la connaissance du protocole de cohérence du cache MESI . Il utilise 4 états pour une ligne de cache: M (odifié), E (xclusive), S (hared), I (nvalid) (et donc il s'appelle MESI), expliqué ci-dessous si nécessaire. Le scénario, expliqué, est le suivant:
Dans tous les cas, une demande de mise en cache peut être bloquée par d'autres processeurs modifiant déjà les données.
la source
J'ai fait un profilage avec la configuration suivante: la machine de test (AMD Athlon64 x2 3800+) a été démarrée, commutée en mode long (interruptions désactivées) et l'instruction d'intérêt a été exécutée en boucle, 100 itérations déroulées et 1000 cycles de boucle. Le corps de la boucle était aligné sur 16 octets. Le temps a été mesuré avec une instruction rdtsc avant et après la boucle. De plus, une boucle fictive sans aucune instruction a été exécutée (qui mesurait 2 cycles par itération de boucle et 14 cycles pour le reste) et le résultat a été soustrait du résultat du temps de profilage des instructions.
Les instructions suivantes ont été mesurées:
lock cmpxchg [rsp - 8], rdx
" (avec correspondance de comparaison et non-correspondance),lock xadd [rsp - 8], rdx
",lock bts qword ptr [rsp - 8], 1
"Dans tous les cas, le temps mesuré était d'environ 310 cycles, l'erreur d'environ +/- 8 cycles
Il s'agit de la valeur pour une exécution répétée sur la même mémoire (mise en cache). Avec un manque de cache supplémentaire, les temps sont considérablement plus élevés. De plus, cela a été fait avec un seul des 2 cœurs actifs, le cache était donc la propriété exclusive et aucune synchronisation du cache n'était nécessaire.
Pour évaluer le coût d'une instruction verrouillée sur un échec de cache, j'ai ajouté une
wbinvld
instruction avant l'instruction verrouillée et mis lewbinvld
plus unadd [rsp - 8], rax
dans la boucle de comparaison. Dans les deux cas, le coût était d'environ 80 000 cycles par paire d'instructions! En cas de verrouillage bts, le décalage horaire était d'environ 180 cycles par instruction.Notez qu'il s'agit du débit réciproque, mais comme les opérations verrouillées sont des opérations de sérialisation, il n'y a probablement aucune différence de latence.
Conclusion: une opération verrouillée est lourde, mais un manque de cache peut être beaucoup plus lourd. Aussi: une opération verrouillée n'entraîne pas d'erreurs de cache. Il ne peut générer du trafic de synchronisation du cache que lorsqu'une ligne de cache n'est pas la propriété exclusive.
Pour démarrer la machine, j'ai utilisé une version x64 de FreeLdr du projet ReactOS. Voici le code source asm:
la source
Sur le SMP basé sur le bus, le préfixe atomique
LOCK
affirme (active) un signal de fil de busLOCK#
. Cela interdira à d'autres processeurs / périphériques sur le bus de l'utiliser.Livre Ppro & P2 http://books.google.com/books?id=3gDmyIYvFH4C&pg=PA245&dq=lock+instruction+pentium&lr=&ei=_E61S5ehLI78zQSzrqwI&cd=1#v=onepage&q=lock%20instruction%20pentium&f= 24 pages
la source