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.
quelle
/proc/sys/kernel/*
funktioniert das Ändern eines Parameters von ? Wenn Sie etwas finden, das funktioniert, fügen Sie diese Konfiguration/etc/sysctl.conf
oder eine Datei ein/etc/sysctl.d/
, damit sie auch nach einem Neustart erhalten bleibt.Antworten:
Die Lösung für das Problem mit der
intel_idle
Weckleistung des fehlerhaften Threads in den letzten Kerneln hat mit dem Wechsel zum CPU-Treiber vonacpi_idle
, dem in älteren Kerneln verwendeten Treiber , zu tun . Leiderintel_idle
ignoriert 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_driver
Datei wie folgt in den folgendencpuidle
Abschnitt einordnen/sys/devices/system/cpu
: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_DEFAULT
Eintrag in hinzufügen/etc/default/grub
und dann ausführenupdate-grub
. Die hinzuzufügenden Startparameter sind:Hier sind die wichtigsten Details zu den drei Startoptionen:
Wenn
intel_idle.max_cstate
Sie den Wert auf Null setzen, wird Ihr cpuidle-Treiber entweder aufacpi_idle
(zumindest gemäß der Dokumentation der Option) zurückgesetzt oder vollständig deaktiviert. Auf meiner Box ist es vollständig deaktiviert (dh das Anzeigen dercurrent_driver
Datei in/sys/devices/system/cpu/cpuidle
erzeugt eine Ausgabe vonnone
). In diesem Fall ist die zweite Startoption nichtprocessor.max_cstate=0
erforderlich. In der Dokumentation heißt es jedoch, dass das Setzen von max_cstate auf Null für denintel_idle
Treiber das Betriebssystem auf denacpi_idle
Treiber zurücksetzen sollte . Daher habe ich für alle Fälle die zweite Startoption eingegeben.Die
processor.max_cstate
Option setzt den maximalen C-Status für denacpi_idle
Treiber auf Null und deaktiviert ihn hoffentlich auch. Ich habe kein System, auf dem ich dies testen kann, daintel_idle.max_cstate=0
der CPU-Treiber auf der gesamten mir zur Verfügung stehenden Hardware vollständig ausgeschaltet ist. Wenn Ihre Installation Sieintel_idle
jedochacpi_idle
nur mit der ersten Startoption von auf zurücksetzt, lassen Sie mich bitte wissen, ob die zweite Optionprocessor.max_cstate
das getan hat, was in den Kommentaren dokumentiert wurde, damit ich diese Antwort aktualisieren kann.Schließlich der letzte der drei Parameter,
idle=poll
ist 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 zuAktualisieren:
Nach zusätzlichen Tests mit verschiedenen
idle=*
Parametern habe ich festgestellt, dass die Einstellungidle
auf,mwait
wenn von Ihrer Hardware unterstützt, eine viel bessere Idee ist. Es scheint, dass die Verwendung derMWAIT/MONITOR
Anweisungen es der CPU ermöglicht, C1E einzugeben, ohne dass eine merkliche Latenz zur Weckzeit des Threads hinzugefügt wird. Mit erhaltenidle=mwait
Sie kühlere CPU-Temperaturen (im Vergleich zuidle=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:Die Verwendung von
idle=mwait
anstelle vonidle=poll
kann 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
mwait
Leerlaufoption 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 funktionierenmwait
, aber testen Sie, ob dies bei Ihrer Hardware der Fall ist. DerHLT
Befehl entspricht fast einemMWAIT
mit 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.quelle
Vielleicht ist Futex, der Baustein für Bedingungsvariablen, langsamer geworden. Dies wird etwas Licht ins Dunkel bringen:
dann
Hier werden die für die interessanten Systemaufrufe benötigten Mikrosekunden nach Zeit sortiert angezeigt.
Auf Kernel 2.6.32
Auf dem Kernel 3.1.9
ich habe das gefunden 5 Jahre alten Fehlerbericht gefunden , der einen vergleichbaren "Ping Pong" -Leistungstest enthält
Ich musste hinzufügen
um zu kompilieren, was ich mit diesem Befehl gemacht habe
Auf Kernel 2.6.32
Auf dem Kernel 3.1.9
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
Auf dem Kernel 3.1.9
quelle
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
quelle