97 votes

Les commutateurs de contexte sont beaucoup plus lents dans les nouveaux noyaux linux

Nous souhaitons mettre à niveau le système d'exploitation de nos serveurs d'Ubuntu 10.04 LTS vers Ubuntu 12.04 LTS. Malheureusement, il semble que la latence pour exécuter un thread qui est devenu exécutable a augmenté de manière significative entre le noyau 2.6 et le noyau 3.2. En fait, les chiffres de latence que nous obtenons sont difficiles à croire.

Permettez-moi d'être plus précis sur le test. Nous avons un programme qui exécute deux threads. Le premier thread obtient l'heure actuelle (en ticks en utilisant RDTSC) et signale une variable de condition une fois par seconde. Le second thread attend la variable de condition et se réveille lorsqu'il est signalé. Il obtient alors l'heure actuelle (en ticks en utilisant RDTSC). La différence entre le temps dans le second thread et le temps dans le premier thread est calculée et affichée sur la console. Après cela, le deuxième thread attend à nouveau la variable de condition. Il sera à nouveau signalé par le premier thread au bout d'une seconde environ.

Donc, en résumé, nous obtenons un communication entre fils via une variable de condition Il en résulte une mesure de la latence une fois par seconde.

Dans le noyau 2.6.32, cette latence est de l'ordre de 2,8 à 3,5 us, ce qui est raisonnable. Dans le noyau 3.2.0, cette latence est passée à environ 40-100 us. J'ai exclu toute différence de matériel entre les deux hôtes. Ils fonctionnent sur du matériel identique (processeurs dual socket X5687 {Westmere-EP} cadencés à 3,6 GHz avec hyperthreading, speedstep et tous les états C désactivés). L'application de test modifie l'affinité des threads pour les exécuter sur des cœurs physiques indépendants du même socket (c'est-à-dire que le premier thread est exécuté sur le cœur 0 et le second sur le cœur 1), il n'y a donc pas de rebond des threads sur les cœurs ou de rebond/communication entre les sockets.

La seule différence entre les deux hôtes est que l'un exécute Ubuntu 10.04 LTS avec le noyau 2.6.32-28 (la boîte de commutation contextuelle rapide) et l'autre exécute la dernière Ubuntu 12.04 LTS avec le noyau 3.2.0-23 (la boîte de commutation contextuelle lente). Tous les paramètres du BIOS et le matériel sont identiques.

Y a-t-il eu des changements dans le noyau qui pourraient expliquer ce ralentissement ridicule du temps nécessaire à l'exécution programmée d'un thread ?

Mise à jour : Si vous souhaitez effectuer le test sur votre hôte et votre build linux, j'ai J'ai posté le code sur pastebin pour votre lecture. Compilez avec :

g++ -O3 -o test_latency test_latency.cpp -lpthread

Exécuter avec (en supposant que vous avez au moins une boîte à double cœur) :

./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1

Mise à jour 2 : Après de nombreuses recherches dans les paramètres du noyau, les messages sur les modifications du noyau et des recherches personnelles, j'ai trouvé quel est le problème et j'ai posté la solution en tant que réponse à cette question.

93voto

Michael Goldshteyn Points 24679

La solution à la problème de performance au réveil d'un mauvais fil dans les noyaux récents est liée à l'adoption de l'option intel_idle pilote cpuidle de acpi_idle le pilote utilisé dans les anciens noyaux. Malheureusement, le intel_idle Le pilote ignore la configuration du BIOS de l'utilisateur pour les états C et danse à son propre rythme . En d'autres termes, même si vous désactivez complètement tous les états C dans le BIOS de votre PC (ou de votre serveur), ce pilote les activera pendant les périodes de brève inactivité, ce qui se produit presque toujours à moins qu'un benchmark synthétique consommant tous les cœurs (par exemple, le stress) ne soit en cours d'exécution. Vous pouvez surveiller les transitions d'état de C, ainsi que d'autres informations utiles liées aux fréquences des processeurs, en utilisant le merveilleux Google outil i7z sur la plupart des matériels compatibles.

Pour voir quel pilote de processeur est actuellement actif dans votre installation, il suffit de cat. current_driver dans le fichier cpuidle section de /sys/devices/system/cpu comme suit :

cat /sys/devices/system/cpu/cpuidle/current_driver

Si vous voulez que votre système d'exploitation Linux moderne ait la latence de changement de contexte la plus faible possible, ajoutez les paramètres de démarrage du noyau suivants pour désactiver toutes ces fonctions d'économie d'énergie :

Sur Ubuntu 12.04, vous pouvez le faire en les ajoutant au fichier GRUB_CMDLINE_LINUX_DEFAULT entrée dans /etc/default/grub et ensuite exécuter update-grub . Les paramètres de démarrage à ajouter sont les suivants :

intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

Voici les détails sanglants de ce que font les trois options de démarrage :

Réglage de intel_idle.max_cstate à zéro va soit rétablir votre pilote de processeur à acpi_idle (du moins selon la documentation de l'option), ou la désactiver complètement. Sur ma boîte, elle est complètement désactivée (c'est-à-dire qu'elle affiche le message current_driver dans /sys/devices/system/cpu/cpuidle produit une sortie de none ). Dans ce cas, la deuxième option de démarrage, processor.max_cstate=0 n'est pas nécessaire. Cependant, la documentation indique que le fait de mettre max_cstate à zéro pour la fonction intel_idle devrait ramener le système d'exploitation à la version acpi_idle pilote. Par conséquent, j'ai mis la deuxième option de démarrage juste au cas où.

El processor.max_cstate L'option définit l'état C maximum pour le acpi_idle à zéro, en espérant le désactiver également. Je ne dispose pas d'un système sur lequel je peux tester cela, parce que intel_idle.max_cstate=0 met complètement hors service le pilote cpuidle sur tout le matériel dont je dispose. Cependant, si votre installation vous fait passer de intel_idle a acpi_idle avec seulement la première option de démarrage, veuillez me faire savoir si la deuxième option, processor.max_cstate a fait ce qu'il était documenté pour faire dans les commentaires afin que je puisse mettre à jour cette réponse.

Enfin, le dernier des trois paramètres, idle=poll est un vrai consommateur d'énergie. Il désactivera C1/C1E, ce qui supprimera le dernier bout de latence restant au prix d'une consommation d'énergie beaucoup plus importante, donc n'utilisez ce dernier que lorsque c'est vraiment nécessaire. Pour la plupart des gens, ce sera excessif, puisque la latence C1* n'est pas si importante que cela. En utilisant mon application de test fonctionnant sur le matériel que j'ai décrit dans la question initiale, la latence est passée de 9 us à 3 us. Il s'agit certainement d'une réduction significative pour les applications très sensibles à la latence (par exemple, les transactions financières, la télémétrie/le suivi de haute précision, l'acquisition de données à haute fréquence, etc...), mais cela ne vaut peut-être pas la peine d'investir dans la puissance électrique pour la grande majorité des applications de bureau. La seule façon d'en être sûr est de comparer l'amélioration des performances de votre application à l'augmentation réelle de la consommation d'énergie et de la chaleur de votre matériel et de peser le pour et le contre.

Mise à jour :

Après des tests supplémentaires avec divers idle=* j'ai découvert que le réglage des paramètres idle a mwait si elle est supportée par votre matériel est une bien meilleure idée. Il semble que l'utilisation de la MWAIT/MONITOR permet à l'UC d'entrer en C1E sans qu'une latence notable ne soit ajoutée au temps de réveil du fil. Avec idle=mwait vous obtiendrez des températures de processeur plus basses (par rapport à l'option idle=poll ), moins de consommation d'énergie, tout en conservant les excellentes latences faibles d'une boucle de polling idle. Par conséquent, l'ensemble des paramètres de démarrage que je recommande pour obtenir une faible latence de réveil des threads du CPU est le suivant :

intel_idle.max_cstate=0 processor.max_cstate=0 idle=mwait

L'utilisation de idle=mwait au lieu de idle=poll peut également aider au lancement de Turbo Boost (en aidant le CPU à rester en dessous de son TDP [Thermal Design Power]) et de l'hyperthreading (pour lequel MWAIT est le mécanisme idéal pour ne pas consommer un cœur physique entier tout en évitant les états C plus élevés). Cela doit encore être prouvé par des tests, ce que je vais continuer à faire.

Mise à jour 2 :

El mwait supprimé des noyaux 3.x plus récents (merci à l'utilisateur ck_ pour la mise à jour). Cela nous laisse avec deux options :

idle=halt - Devrait fonctionner aussi bien que mwait mais testez pour être sûr que c'est le cas avec votre matériel. Le site HLT est presque équivalente à une instruction MWAIT Le problème réside dans le fait qu'une interruption est nécessaire pour sortir d'un état HLT, alors qu'une écriture mémoire (ou une interruption) peut être utilisée pour sortir de l'état MWAIT. Selon ce que le noyau Linux utilise dans sa boucle d'inactivité, cela peut rendre MWAIT potentiellement plus efficace. Donc, comme je l'ai dit, testez/profilez et voyez si cela répond à vos besoins de latence...

y

idle=poll - L'option la plus performante, au détriment de la puissance et de la chaleur.

8voto

amdn Points 5491

Peut-être que ce qui a ralenti est futex, le bloc de construction pour les variables de condition. Cela vous éclairera :

strace -r ./test_latency 0 1 &> test_latency_strace & sleep 8 && killall test_latency

puis

for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done

qui montrera les microsecondes prises pour les appels système intéressants, triés par temps.

Sur le noyau 2.6.32

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000140 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000129 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000124 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000119 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000106 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000103 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000102 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000125 futex(0x7f98ce4c0b88, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000042 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000038 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000030 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000029 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 0
 0.000028 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000027 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000018 futex(0x7fff82f0ec3c, FUTEX_WAKE_PRIVATE, 1) = 0
nanosleep
 0.000027 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, 0x7fff82f0eb40) = ? ERESTART_RESTARTBLOCK (To be restarted)
 0.000017 nanosleep({1, 0}, {1, 0}) = 0
rt_sig
 0.000045 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000040 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000038 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000033 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigaction(SIGRT_1, {0x37f8c052b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000027 rt_sigaction(SIGRTMIN, {0x37f8c05370, [], SA_RESTORER|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000023 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0

Sur le noyau 3.1.9

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000129 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000126 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000122 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000115 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000114 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000112 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000109 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000139 futex(0x3f8b8f2fb0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000043 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000041 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000036 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
nanosleep
 0.000025 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000022 nanosleep({1, 0}, {0, 3925413}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
 0.000021 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
rt_sig
 0.000045 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000044 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000043 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000040 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000038 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000037 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigaction(SIGRT_1, {0x3f892067b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000024 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigaction(SIGRTMIN, {0x3f89206720, [], SA_RESTORER|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0

J'ai trouvé ceci Rapport de bug vieux de 5 ans qui contient un test de performance "ping pong" qui compare

  1. mutex libpthread monofilaire
  2. variable de condition libpthread
  3. de simples signaux Unix

J'ai dû ajouter

#include <stdint.h>

afin de compiler, ce que j'ai fait avec cette commande

g++ -O3 -o condvar-perf condvar-perf.cpp -lpthread -lrt

Sur le noyau 2.6.32

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29085 us; per iteration:   29 ns / 9.4e-05 context switches.
c.v. ping-pong test   elapsed:  4771993 us; per iteration: 4771 ns / 4.03 context switches.
signal ping-pong test elapsed:  8685423 us; per iteration: 8685 ns / 4.05 context switches.

Sur le noyau 3.1.9

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26811 us; per iteration:   26 ns / 8e-06 context switches.
c.v. ping-pong test   elapsed: 10930794 us; per iteration: 10930 ns / 4.01 context switches.
signal ping-pong test elapsed: 10949670 us; per iteration: 10949 ns / 4.01 context switches.

J'en conclus qu'entre les noyaux 2.6.32 et 3.1.9, le changement de contexte a effectivement ralenti, mais pas autant que ce que vous observez dans le noyau 3.2. Je réalise que cela ne répond pas encore à votre question, je vais continuer à creuser.

Edit : J'ai trouvé que changer la priorité en temps réel du processus (les deux threads) améliore les performances sur 3.1.9 pour correspondre à la 2.6.32. Cependant, la même priorité sur la version 2.6.32 la ralentit... allez savoir pourquoi - je vais étudier la question de plus près.

Voici mes résultats maintenant :

Sur le noyau 2.6.32

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29629 us; per iteration:   29 ns / 0.000418 context switches.
c.v. ping-pong test   elapsed:  6225637 us; per iteration: 6225 ns / 4.1 context switches.
signal ping-pong test elapsed:  5602248 us; per iteration: 5602 ns / 4.09 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29049 us; per iteration:   29 ns / 0.000407 context switches.
c.v. ping-pong test   elapsed: 16131360 us; per iteration: 16131 ns / 4.29 context switches.
signal ping-pong test elapsed: 11817819 us; per iteration: 11817 ns / 4.16 context switches.
$ 

Sur le noyau 3.1.9

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26830 us; per iteration:   26 ns / 5.7e-05 context switches.
c.v. ping-pong test   elapsed: 12812788 us; per iteration: 12812 ns / 4.01 context switches.
signal ping-pong test elapsed: 13126865 us; per iteration: 13126 ns / 4.01 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    27025 us; per iteration:   27 ns / 3.7e-05 context switches.
c.v. ping-pong test   elapsed:  5099885 us; per iteration: 5099 ns / 4 context switches.
signal ping-pong test elapsed:  5508227 us; per iteration: 5508 ns / 4 context switches.
$

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X