Der Kontext wechselt in neuen Linux-Kerneln viel langsamer

99

Wir möchten das Betriebssystem auf unseren Servern von Ubuntu 10.04 LTS auf Ubuntu 12.04 LTS aktualisieren. Leider scheint sich die Latenz zum Ausführen eines ausführbaren Threads vom 2.6-Kernel auf den 3.2-Kernel erheblich erhöht zu haben. Tatsächlich sind die Latenzzahlen, die wir erhalten, kaum zu glauben.

Lassen Sie mich den Test genauer beschreiben. Wir haben ein Programm, das zwei Threads ausführt. Der erste Thread erhält die aktuelle Zeit (in Ticks mit RDTSC) und signalisiert dann einmal pro Sekunde eine Bedingungsvariable. Der zweite Thread wartet auf die Bedingungsvariable und wacht auf, wenn er signalisiert wird. Es erhält dann die aktuelle Zeit (in Ticks mit RDTSC). Die Differenz zwischen der Zeit im zweiten Thread und der Zeit im ersten Thread wird berechnet und auf der Konsole angezeigt. Danach wartet der zweite Thread erneut auf die Bedingungsvariable. Es wird vom ersten Thread nach etwa einem zweiten Durchgang erneut signalisiert.

Kurz gesagt, wir erhalten als Ergebnis eine Thread-zu-Thread-Kommunikation über die Messung der bedingungsvariablen Latenz einmal pro Sekunde.

In Kernel 2.6.32 liegt diese Latenz irgendwo in der Größenordnung von 2.8-3.5 us, was vernünftig ist. In Kernel 3.2.0 hat sich diese Latenz auf irgendwo in der Größenordnung von 40-100 us erhöht. Ich habe Unterschiede in der Hardware zwischen den beiden Hosts ausgeschlossen. Sie laufen auf identischer Hardware (Dual-Socket-X5687 {Westmere-EP} -Prozessoren mit 3,6 GHz, Hyperthreading, Speedstep und allen C-Zuständen). Die Test-App ändert die Affinität der Threads, um sie auf unabhängigen physischen Kernen desselben Sockets auszuführen (dh der erste Thread wird auf Core 0 und der zweite Thread auf Core 1 ausgeführt), sodass kein Threading auf Threads erfolgt Kerne oder Bouncing / Kommunikation zwischen Sockets.

Der einzige Unterschied zwischen den beiden Hosts besteht darin, dass auf einem Ubuntu 10.04 LTS mit Kernel 2.6.32-28 (der Box für den schnellen Kontextwechsel) und auf dem anderen das neueste Ubuntu 12.04 LTS mit Kernel 3.2.0-23 (dem langsamen Kontext) ausgeführt wird Schaltkasten). Alle BIOS-Einstellungen und Hardware sind identisch.

Gab es Änderungen im Kernel, die für diese lächerliche Verlangsamung der geplanten Ausführung eines Threads verantwortlich sein könnten?

Update: Wenn Sie den Test auf Ihrem Host- und Linux-Build ausführen möchten, habe ich den Code zur Einsicht in Pastebin gepostet . Kompilieren mit:

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

Führen Sie mit aus (vorausgesetzt, Sie haben mindestens eine Dual-Core-Box):

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

Update 2 : Nach langem Durchsuchen der Kernelparameter, Beiträgen zu Kerneländerungen und persönlichen Recherchen habe ich herausgefunden, wo das Problem liegt, und die Lösung als Antwort auf diese Frage veröffentlicht.

Michael Goldshteyn
quelle
1
Nur eine Vermutung, aber vielleicht /proc/sys/kernel/*funktioniert das Ändern eines Parameters von ? Wenn Sie etwas finden, das funktioniert, fügen Sie diese Konfiguration /etc/sysctl.confoder eine Datei ein /etc/sysctl.d/, damit sie auch nach einem Neustart erhalten bleibt.
Carlos Campderrós
1
Ich habe / proc / sys / kernel zwischen den beiden Hosts verglichen, sehe jedoch keine bedeutenden Unterschiede, insbesondere bei planungsbezogenen Konfigurationselementen.
Michael Goldshteyn
Ich erinnere mich vage an ein Gerücht, dass RDTSC zwischen Kernen nicht unbedingt richtig synchronisiert ist, aber ich würde erwarten, dass sich die Zeit umkehren würde, wenn dies ein Problem wäre. Haben Sie versucht, die Affinitäten zu fummeln, um beide Threads auf demselben Kern auszuführen und zu sehen, was passiert?
David gegeben
Auf Intel-Kernen funktioniert RDTSC einwandfrei über Kerne hinweg, insbesondere über Kerne auf derselben CPU (dh demselben Sockel). Interessanterweise sinken die Latenzen auf dem neueren Kernel auf 4 bis 10 us, wenn beide Threads auf demselben Kern ausgeführt werden. 3 uns auf dem älteren Kernel.
Michael Goldshteyn
Nur ein allgemeiner Kommentar: Wenn Sie sich darauf verlassen, dass TSCs synchronisiert werden, ist dies bestenfalls zweifelhaft. In Ihrem speziellen Fall sollte dies jedoch funktionieren, da Sie zwei Kerne auf einem physischen Chip verwenden.
Twalberg

Antworten:

95

Die Lösung für das Problem mit der intel_idleWeckleistung des fehlerhaften Threads in den letzten Kerneln hat mit dem Wechsel zum CPU-Treiber von acpi_idle, dem in älteren Kerneln verwendeten Treiber , zu tun . Leider intel_idleignoriert der Treiber die BIOS-Konfiguration des Benutzers für die C-Zustände und tanzt nach eigenem Ermessen . Mit anderen Worten, selbst wenn Sie alle C-Zustände im BIOS Ihres PCs (oder Servers) vollständig deaktivieren, werden sie von diesem Treiber in Zeiten kurzer Inaktivität aktiviert, die fast immer auftreten, es sei denn, es handelt sich um einen synthetischen Benchmark, der nur den Kern verbraucht (z. B. Stress) ) läuft. Mit dem wunderbaren Google i7z-Tool auf den meisten kompatiblen Hardwarekomponenten können Sie C- Statusübergänge sowie andere nützliche Informationen zu Prozessorfrequenzen überwachen .

Um zu sehen, welcher CPU-Treiber derzeit in Ihrem Setup aktiv ist, müssen Sie die current_driverDatei wie folgt in den folgenden cpuidleAbschnitt einordnen /sys/devices/system/cpu:

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

Wenn Sie möchten, dass Ihr modernes Linux-Betriebssystem die geringstmögliche Latenz für Kontextwechsel aufweist, fügen Sie die folgenden Kernel-Startparameter hinzu, um alle diese Energiesparfunktionen zu deaktivieren:

Unter Ubuntu 12.04 können Sie dies tun, indem Sie sie dem GRUB_CMDLINE_LINUX_DEFAULTEintrag in hinzufügen /etc/default/grubund dann ausführen update-grub. Die hinzuzufügenden Startparameter sind:

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

Hier sind die wichtigsten Details zu den drei Startoptionen:

Wenn intel_idle.max_cstateSie den Wert auf Null setzen, wird Ihr cpuidle-Treiber entweder auf acpi_idle(zumindest gemäß der Dokumentation der Option) zurückgesetzt oder vollständig deaktiviert. Auf meiner Box ist es vollständig deaktiviert (dh das Anzeigen der current_driverDatei in /sys/devices/system/cpu/cpuidleerzeugt eine Ausgabe von none). In diesem Fall ist die zweite Startoption nicht processor.max_cstate=0erforderlich. In der Dokumentation heißt es jedoch, dass das Setzen von max_cstate auf Null für den intel_idleTreiber das Betriebssystem auf den acpi_idleTreiber zurücksetzen sollte . Daher habe ich für alle Fälle die zweite Startoption eingegeben.

Die processor.max_cstateOption setzt den maximalen C-Status für den acpi_idleTreiber auf Null und deaktiviert ihn hoffentlich auch. Ich habe kein System, auf dem ich dies testen kann, da intel_idle.max_cstate=0der CPU-Treiber auf der gesamten mir zur Verfügung stehenden Hardware vollständig ausgeschaltet ist. Wenn Ihre Installation Sie intel_idlejedoch acpi_idlenur mit der ersten Startoption von auf zurücksetzt, lassen Sie mich bitte wissen, ob die zweite Option processor.max_cstatedas getan hat, was in den Kommentaren dokumentiert wurde, damit ich diese Antwort aktualisieren kann.

Schließlich der letzte der drei Parameter, idle=pollist ein echtes Kraftschwein. Dadurch wird C1 / C1E deaktiviert, wodurch die letzte verbleibende Latenz auf Kosten eines viel höheren Stromverbrauchs beseitigt wird. Verwenden Sie diese also nur, wenn dies wirklich erforderlich ist. Für die meisten ist dies ein Overkill, da die C1 * -Latenz nicht allzu groß ist. Unter Verwendung meiner Testanwendung, die auf der Hardware ausgeführt wird, die ich in der ursprünglichen Frage beschrieben habe, stieg die Latenz von 9 auf 3 us. Dies ist sicherlich eine erhebliche Reduzierung für hoch latenzempfindliche Anwendungen (z. B. Finanzhandel, hochpräzise Telemetrie / Tracking, Datenerfassung mit hoher Frequenz usw.), ist jedoch für die überwiegende Mehrheit von möglicherweise nicht den anfallenden Stromschlag wert Desktop-Apps. Die einzige Möglichkeit, dies sicher zu wissen, besteht darin, die Leistungsverbesserung Ihrer Anwendung im Vergleich zu

Aktualisieren:

Nach zusätzlichen Tests mit verschiedenen idle=*Parametern habe ich festgestellt, dass die Einstellung idleauf, mwaitwenn von Ihrer Hardware unterstützt, eine viel bessere Idee ist. Es scheint, dass die Verwendung der MWAIT/MONITORAnweisungen es der CPU ermöglicht, C1E einzugeben, ohne dass eine merkliche Latenz zur Weckzeit des Threads hinzugefügt wird. Mit erhalten idle=mwaitSie kühlere CPU-Temperaturen (im Vergleich zu idle=poll), weniger Stromverbrauch und behalten dennoch die hervorragenden niedrigen Latenzen einer Polling-Leerlaufschleife bei. Daher lautet mein aktualisierter empfohlener Satz von Startparametern für eine niedrige Wecklatenz des CPU-Threads basierend auf diesen Ergebnissen:

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

Die Verwendung von idle=mwaitanstelle von idle=pollkann auch bei der Initiierung von Turbo Boost (indem die CPU unter ihrer TDP (Thermal Design Power) bleibt) und Hyperthreading (für die MWAIT der ideale Mechanismus ist, um nicht einen gesamten physischen Kern zu verbrauchen, während es gleichzeitig verbraucht wird) hilfreich sein Zeit, die die höheren C-Zustände vermeidet). Dies muss jedoch noch in Tests bewiesen werden, was ich auch weiterhin tun werde.

Update 2:

Die mwaitLeerlaufoption wurde aus neueren 3.x-Kerneln entfernt (danke an Benutzer ck_ für das Update). Das lässt uns zwei Möglichkeiten:

idle=halt- Sollte genauso gut funktionieren mwait, aber testen Sie, ob dies bei Ihrer Hardware der Fall ist. Der HLTBefehl entspricht fast einem MWAITmit dem Statushinweis 0. Das Problem liegt in der Tatsache, dass ein Interrupt erforderlich ist, um aus einem HLT-Status herauszukommen, während ein Speicherschreibvorgang (oder Interrupt) verwendet werden kann, um aus dem MWAIT-Status herauszukommen. Abhängig davon, was der Linux-Kernel in seiner Leerlaufschleife verwendet, kann dies MWAIT potenziell effizienter machen. Also, wie gesagt, Test / Profil und sehen, ob es Ihren Latenzanforderungen entspricht ...

und

idle=poll - Die leistungsstärkste Option auf Kosten von Strom und Wärme.

Michael Goldshteyn
quelle
Entschuldigung, aber warum haben Sie erwartet, dass C-Zustände von der Firmware verwaltet werden? Suspend-Zustände sind Laufzeitzustände und werden vom Betriebssystem per Design verwaltet. Wie Sie festgestellt haben, verwenden Sie es nicht, wenn Sie keine Laufzeitsperre möchten.
Andy Ross
6
Entschuldigung, aber C-Zustände, EIST und C1E können im BIOS deaktiviert werden. Ich erwarte, dass das Betriebssystem meine BIOS-Einstellungen berücksichtigt. Dies gilt insbesondere angesichts der schrecklichen Werkzeuge und Dokumentationen in diesem Fall.
Michael Goldshteyn
4
Vielleicht über Ihr BIOS ausgeschaltet. Ich kenne nichts in einer relevanten Spezifikation, das dies erfordert. Entschuldigung, aber wenn Sie etwas vom BIOS "erwarten" , werden Sie wiederholt gebissen. Das Beste, was Firmware in einem modernen PC tun kann, ist nichts. Es tut mir leid, dass Sie überrascht waren, aber ehrlich gesagt ist dies ein Benutzerfehler. Ihr Benchmark war das Messen der Suspend- und Resume-Zeiten.
Andy Ross
19
Eine der Rollen der BIOS-Funktionsauswahl besteht darin, Geräte zu aktivieren / deaktivieren. In einigen Fällen wird diese Auswahl dem Betriebssystem auferlegt (z. B. USB auf dem Motherboard, eSATA und Netzwerkkarten). In anderen Fällen wird vom Betriebssystem erwartet, dass es Ihre Wünsche berücksichtigt (z. B. EIST, C-Status, Hyperthreading, Execute Disable, AES-NI, Virtualisierung usw.). Das BIOS bietet eine einzige zentrale Oberfläche für die Geräte- / Funktionsauswahl, die betriebssystemneutral ist. Auf diese Weise kann der Benutzer mehrere (möglicherweise sehr unterschiedliche) Betriebssysteme auf dem Host installieren, die alle dieselben Hardwarefunktionen verwenden. Diese Antwort ist jedoch subjektiv und muss zustimmen, um nicht zuzustimmen.
Michael Goldshteyn
1
idle = mwait wird im aktuellen 3.x-Kernel nicht mehr unterstützt lkml.org/lkml/2013/2/10/21 alternative Ratschläge?
ck_
8

Vielleicht ist Futex, der Baustein für Bedingungsvariablen, langsamer geworden. Dies wird etwas Licht ins Dunkel bringen:

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

dann

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

Hier werden die für die interessanten Systemaufrufe benötigten Mikrosekunden nach Zeit sortiert angezeigt.

Auf Kernel 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

Auf dem Kernel 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

ich habe das gefunden 5 Jahre alten Fehlerbericht gefunden , der einen vergleichbaren "Ping Pong" -Leistungstest enthält

  1. Libpthread-Mutex mit einem Thread
  2. libpthread-Bedingungsvariable
  3. einfache alte Unix-Signale

Ich musste hinzufügen

#include <stdint.h>

um zu kompilieren, was ich mit diesem Befehl gemacht habe

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

Auf Kernel 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.

Auf dem Kernel 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.

Ich komme zu dem Schluss, dass sich der Kontextwechsel zwischen Kernel 2.6.32 und 3.1.9 tatsächlich verlangsamt hat, wenn auch nicht so stark, wie Sie es in Kernel 3.2 beobachten. Mir ist klar, dass dies Ihre Frage noch nicht beantwortet. Ich werde weiter graben.

Bearbeiten: Ich habe festgestellt, dass das Ändern der Echtzeitpriorität des Prozesses (beide Threads) die Leistung von 3.1.9 auf 2.6.32 verbessert. Wenn Sie jedoch die gleiche Priorität auf 2.6.32 setzen, wird diese langsamer ... siehe Abbildung - ich werde mich näher damit befassen.

Hier sind meine Ergebnisse jetzt:

Auf Kernel 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.
$ 

Auf dem Kernel 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.
$ 
amdn
quelle
Ich habe es auf Fedora und CentOS ausgeführt, habe kein Ubuntu. Ich werde meine Ergebnisse veröffentlichen.
Amdn
OK, ich habe es auf beiden Hosts (dh und verschiedenen Kerneln) ausgeführt und die Ergebnisse zeigen fast keine Unterschiede. Dieser Test zeigte also keine Unterschiede auf. Die Futex-Aufrufzeit unterscheidet sich in der vierten Dezimalstelle - eine unbedeutende Leistungsminderung. Äh, warte, sind die ganzen Zahlen in Sekunden? Ich habe gerade gesehen, dass Sie Ihre Ergebnisse veröffentlicht haben und sie meinen ähnlich sehen ...
Michael Goldshteyn
Ok, das schließt die Implementierung von Futex aus - wir kehren zu Ihrer Theorie des Kontextwechsels zurück. Sie können diese Antwort jederzeit löschen, da sie wirklich in Kommentare gehört. Ich wollte nur die Möglichkeit, die Befehle zu formatieren.
Amdn
Ja, die Zeiten sind in Sekunden angegeben. Die Aufrufe von Futex, die länger als eine Sekunde dauern, beziehen sich auf den Thread, der auf die Bedingung wartet.
Amdn
Was ist, wenn Sie etwas aus den Ergebnissen entnehmen?
Michael Goldshteyn
1

Möglicherweise sehen Sie auch Prozessoren, die in neueren Prozessen und Linux-Kerneln aufgrund des von c-Zuständen getrennten pstate- Treibers nach unten klicken . Um dies zu deaktivieren, haben Sie zusätzlich den folgenden Kernel-Parameter:

intel_pstate=disable

Kyle Brandt
quelle