Meine Grundannahme ist, dass, wenn die einzigen einschränkenden Faktoren eines Prozesses Festplatte und CPU sind, die Gesamtsystem- "iowait" + CPU-Auslastung mindestens 100% einer logischen CPU entsprechen sollte. (In anderen Fällen gilt dies nicht. ZB beim Herunterladen einer Datei mitwget
, ist das Netzwerk häufig der einschränkende Faktor.)
Diese Annahme wird durch einen einfachen Test verletzt. Wird das erwartet? Wenn es erwartet wird, gibt es eine Reihe von Bedingungen, unter denen ich davon ausgehen sollte, dass meine Annahme zutrifft?
Hier gibt es einige Hintergrundinformationen zu "iowait": Woher weiß eine CPU, dass ein E / A-Vorgang ansteht? Die Antwort hier zitiert die kontraintuitive Idee, dass das kumulative iowait "unter bestimmten Bedingungen abnehmen kann". Ich frage mich, ob mein einfacher Test einen solchen undokumentierten Zustand auslösen kann.
UPDATE : Bitte fahren Sie mit der Antwort fort .
Die Antwort hat einen einfacheren Test als den, den ich ursprünglich verwendet habe. Ich habe die ursprüngliche Frage unten beibehalten. Die ursprüngliche Frage könnte einige zusätzliche Details enthalten.
Ursprüngliche Frage
In einem kurzen Test fordere ich dd
den Kernel auf, zufällige Bytes zu generieren und diese in eine Datei zu schreiben. Ich führe den dd
Befehl im perf stat
Kernel aus, um zu sehen, wie viel CPU-Zeit im Kernel verbracht wurde. Ich führe es auch drinnen aus perf trace -s
, um die darin verbrachte Zeit zu melden write()
. Gleichzeitig vmstat 5
starte ich in einem anderen Terminal, um das System "iowait" zu sehen.
- Ich habe erwartet, dass ich mindestens eine ganze CPU als "nicht im Leerlauf" ansehen würde, dh 100% der Zeit, in der sie entweder läuft oder angehalten wird, aber auf E / A wartet ("iowait" -Zustand). Es war nicht.
- (Außerdem hatte ich erwartet, dass die "iowait" -Zeit in etwa mit der Zeit übereinstimmt, die beim Schreiben () verbracht wurde. Dies schien jedoch nicht der Fall zu sein.)
Die detaillierten Ergebnisse und die Testumgebung sind nachstehend aufgeführt. Gezeigt wird auch ein alternativer Test, bei dem meine Vermutung zutraf. Hinweis: Es musste perf stat
drinnen gelaufen werden perf trace
, nicht umgekehrt. Dies wird hier detailliert beschrieben: Zeigt "perf stat" (und "time"!) Falsche Ergebnisse, wenn "perf trace - s" ausgeführt wird?
Hintergrundinformationen zu "iowait"
Die folgende Definition stammt aus der
sar
Manpage:% iowait:
Prozentsatz der Zeit, in der sich die CPU oder CPUs im Leerlauf befanden, während der das System eine ausstehende Festplatten-E / A-Anforderung hatte.
% Iowait bedeutet daher, dass aus CPU-Sicht keine Tasks ausgeführt werden konnten, aber mindestens eine E / A-Operation ausgeführt wurde. iowait ist einfach eine Form der Leerlaufzeit, in der nichts geplant werden konnte. Der Wert kann für die Anzeige eines Leistungsproblems nützlich sein oder auch nicht, teilt dem Benutzer jedoch mit, dass das System inaktiv ist und möglicherweise mehr Arbeit in Anspruch genommen hat.
https://support.hpe.com/hpsc/doc/public/display?docId=c02783994
Es gibt auch einen längeren Artikel: Grundlegendes zum E / A-Warten (oder warum 0% Leerlauf in Ordnung sein kann) . Dies erklärt, wie Sie die Definition anhand des Kernel-Codes klar erkennen können. Der Code hat sich etwas geändert, aber die Idee ist noch klar:
/*
* Account for idle time.
* @cputime: the CPU time spent in idle wait
*/
void account_idle_time(u64 cputime)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
struct rq *rq = this_rq();
if (atomic_read(&rq->nr_iowait) > 0)
cpustat[CPUTIME_IOWAIT] += cputime;
else
cpustat[CPUTIME_IDLE] += cputime;
}
Der Artikel zeigt auch eine Reihe verwandter Experimente mit einem System mit einer CPU. Einige der Experimente verwenden sogar dd
mit if=/dev/urandom
! Die Experimente beinhalten jedoch nicht meinen Test dd if=/dev/urandom of=test.out
. Es nutzt nur dd if=/dev/urandom of=/dev/null
.
"IO wait" ist etwas kniffliger zu überlegen, da wir Multi-CPU-Systeme verwenden, aber ich glaube, ich verstehe es immer noch, basierend auf dem zitierten Code.
Umgebung
Ich habe vier logische CPUs.
Ich benutze LVM und das ext4-Dateisystem. Ich verwende keine Verschlüsselung auf meiner Festplatte oder meinem Dateisystem. Ich habe überhaupt kein Netzwerk-Dateisystem gemountet, daher lese oder schreibe ich kein Netzwerk-Dateisystem.
Die folgenden Ergebnisse stammen vom Kernel 4.20.15-200.fc29.x86_64
, der den noop
IO-Scheduler verwendet. Der cfq
IO-Scheduler liefert ähnliche Ergebnisse.
(Ich habe auch ähnliche Ergebnisse bei einem Kernel-Build gesehen, der auf einer ähnlichen Konfiguration basierte, aber näher an der Kernel-Version 5.1 lag und verwendet mq-deadline
. Also wurde der neue blk-mq
Code verwendet).
Test und Ergebnisse
$ sudo perf trace -s \
perf stat \
dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000
3000+0 records in
3000+0 records out
3145728000 bytes (3.1 GB, 2.9 GiB) copied, 31.397 s, 100 MB/s
Performance counter stats for 'dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000':
18,014.26 msec task-clock # 0.574 CPUs utilized
3,199 context-switches # 0.178 K/sec
4 cpu-migrations # 0.000 K/sec
328 page-faults # 0.018 K/sec
45,232,163,658 cycles # 2.511 GHz
74,538,278,379 instructions # 1.65 insn per cycle
4,372,725,344 branches # 242.737 M/sec
4,650,429 branch-misses # 0.11% of all branches
31.398466725 seconds time elapsed
0.006966000 seconds user
17.910332000 seconds sys
Summary of events:
...
dd (4620), 12156 events, 12.0%
syscall calls total min avg max stddev
(msec) (msec) (msec) (msec) (%)
--------------- -------- --------- --------- --------- --------- ------
read 3007 17624.985 0.002 5.861 12.345 0.21%
write 3003 13722.837 0.004 4.570 179.928 2.63%
openat 12 0.371 0.002 0.031 0.267 70.36%
...
Ich las die iowait
Figur aus der wa
Spalte von vmstat
. Sie können anhand der io
Spalte erkennen, wann der Test ausgeführt wird ( bo
= 1K blockiert die Ausgabe).
$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 5126892 176512 1486060 0 0 1788 4072 321 414 4 4 83 9 0
1 0 0 5126632 176520 1485988 0 0 0 7 212 405 0 1 99 0 0
0 0 0 5126884 176520 1485988 0 0 0 0 130 283 0 0 99 0 0
0 0 0 5126948 176520 1485908 0 0 0 1 157 325 0 0 99 0 0
0 0 0 5126412 176520 1486412 0 0 115 0 141 284 0 0 99 0 0
0 2 0 5115724 176548 1487056 0 0 0 6019 18737 10733 3 6 89 2 0
1 0 0 5115708 176580 1487104 0 0 3 91840 1276 990 0 13 77 9 0
1 0 0 5115204 176600 1487128 0 0 2 91382 1382 1014 0 14 81 4 0
1 0 0 5115268 176636 1487084 0 0 4 88281 1257 901 0 14 83 3 0
0 1 0 5113504 177028 1487764 0 0 77 92596 1374 1111 0 15 83 2 0
1 0 0 5114008 177036 1487768 0 0 0 113282 1460 1060 0 16 81 2 0
1 0 0 5113472 177044 1487792 0 0 0 110821 1489 1118 0 16 74 10 0
0 0 0 5123852 177068 1487896 0 0 0 20537 631 714 1 3 94 2 0
0 0 0 5123852 177076 1487856 0 0 0 10 324 529 2 1 98 0 0
2 0 0 5123852 177084 1487872 0 0 0 70 150 299 0 0 99 0 0
Testergebnisse dort, wo sie gültig sind (innerhalb einer VM)
Ich habe den gleichen Test in einer VM mit 1 CPU versucht, die den Kernel ausführte 5.0.9-301.fc30.x86_64
und verwendete mq-deadline
(und daher blk-mq). In diesem Test hat es so funktioniert, wie ich es erwartet hatte.
$ sudo perf trace -s \
perf stat \
dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000
[sudo] password for alan-sysop:
3000+0 records in
3000+0 records out
3145728000 bytes (3.1 GB, 2.9 GiB) copied, 46.8071 s, 67.2 MB/s
Performance counter stats for 'dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000':
18,734.89 msec task-clock # 0.400 CPUs utilized
16,690 context-switches # 0.891 K/sec
0 cpu-migrations # 0.000 K/sec
328 page-faults # 0.018 K/sec
<not supported> cycles
<not supported> instructions
<not supported> branches
<not supported> branch-misses
46.820355993 seconds time elapsed
0.011840000 seconds user
18.531449000 seconds sys
Summary of events:
...
dd (1492), 12156 events, 38.4%
syscall calls total min avg max stddev
(msec) (msec) (msec) (msec) (%)
--------------- -------- --------- --------- --------- --------- ------
write 3003 28269.070 0.019 9.414 5764.657 22.39%
read 3007 18371.469 0.013 6.110 14.848 0.53%
execve 6 10.399 0.012 1.733 10.328 99.18%
...
Ausgabe von vmstat 5
:
$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 726176 52128 498508 0 0 2040 231 236 731 7 5 77 11 0
0 0 0 726176 52136 498508 0 0 0 10 25 46 0 0 99 1 0
0 0 0 726208 52136 498508 0 0 0 0 29 56 0 0 100 0 0
0 1 0 702280 55944 511780 0 0 2260 13109 4399 9049 3 17 55 25 0
0 1 0 701776 56040 511960 0 0 18 129582 1406 1458 0 73 0 27 0
0 2 0 701524 56156 512168 0 0 22 87060 960 991 0 50 0 50 0
3 1 0 701524 56228 512328 0 0 14 118170 1301 1322 0 68 0 32 0
1 1 0 701272 56260 512392 0 0 6 86426 994 982 0 53 0 46 0
0 2 0 701020 56292 512456 0 0 6 56115 683 660 0 37 0 63 0
3 2 0 700540 56316 512504 0 0 5 33450 446 457 0 26 0 74 0
0 2 0 700860 56332 512536 0 0 3 16998 311 240 0 19 0 81 0
1 2 0 700668 56368 512616 0 0 7 32563 443 428 0 24 0 76 0
1 0 0 700668 56392 512648 0 0 3 20338 245 272 0 12 0 88 0
0 1 0 707096 56408 512920 0 0 54 20913 312 530 0 12 79 8 0
0 0 0 707064 56432 512920 0 0 0 49 39 64 0 0 45 55 0
0 0 0 707064 56432 512920 0 0 0 0 24 46 0 0 100 0 0
0 0 0 707064 56432 512920 0 0 0 80 28 47 0 0 100 0 0
Ich habe versucht, der VM eine CPU im laufenden Betrieb hinzuzufügen und erneut zu testen. Die Ergebnisse waren variabel: Manchmal zeigte es ungefähr 0% in der Leerlaufspalte, und manchmal zeigte es ungefähr 50% Leerlauf (dh eine von zwei CPUs). Bei 0% "Leerlauf" war "iowait" sehr hoch, dh mehr als eine CPU wert. Dh mein Erwartungspunkt 2 war nicht korrekt. Ich kann diese offensichtliche Einschränkung von "iowait" auf Multi-CPU-Systemen widerwillig akzeptieren . (Obwohl ich es nicht ganz verstehe. Wenn jemand es genau erklären möchte, wäre das großartig.) "Leerlauf" lag jedoch in beiden Fällen nicht über 50%, sodass diese Tests immer noch mit meiner ersten Annahme über "iowait" übereinstimmten.
Ich habe versucht, die VM herunterzufahren und mit 4 CPUs zu starten. In ähnlicher Weise hatte ich oft genau 75% Leerlauf und manchmal nur 50% Leerlauf, aber ich sah nicht mehr als 75% Leerlauf (dh mehr als drei von vier CPUs).
Während ich auf dem physischen System mit 4 CPUs das Ergebnis von mehr als 80% Leerlauf, wie oben gezeigt, immer noch reproduzieren kann.
this_rq()->nr_iowait
ist die Anzahl der Aufgaben, die nurio_schedule()
auf der aktuellen CPU verwendet werden . Liege ich falsch?iowait
versucht, die Zeit zu messen, die auf E / A im Allgemeinen gewartet wird. Sie wird nicht von einer bestimmten CPU verfolgt und kann es auch nicht sein" . Lassen Sie mich betonen, dass ich mir dessen nicht sicher bin und nur meine Überraschung ausdrücke.atop
oder ausführen ,atopsar -c 5
werden die CPU-Auslastungszahlen angezeigt . Sie enthalten iowait und die iowait-Werte pro CPU können unterschiedliche Werte ungleich Null anzeigen :-). Odersar -P ALL 1
wenn Sie nicht verwendenatop
. Dies ist die Art und Weise, wie dasiowait
Modell für Multi-CPU-Systeme erweitert wurde. Ich bin nicht sicher, ob dieses Modell tatsächlich verwendet werden kann oder ob der iowait-Code auf diese Weise weiterhin funktioniert, wenn nur eine CPU vorhanden ist online, aber sonst ist es einfach nicht vertrauenswürdig.Antworten:
Hinweis zum Inhalt : Dieser Beitrag enthält Links zu verschiedenen Linux-Diskussionen und -Codes. Einige verlinkte Inhalte entsprechen nicht dem aktuellen Verhaltenskodex für StackExchange oder Linux . Meistens "beleidigen sie den Code [aber nicht die Person]". Es wird jedoch eine Sprache verwendet, die einfach nicht wiederholt werden sollte. Ich bitte Sie, diese Sprache nicht zu imitieren, zu papageien oder zu debattieren.
Ich habe mein Problem gefunden. Es wurde bereits vor fünf Jahren bemerkt, und es wäre nicht trivial zu beheben.
Die "iowait" -Zeit wird durch die folgende Funktion aktualisiert
account_idle_time()
:Dies funktioniert wie erwartet, wenn Sie die CPU-Zeit durch "Abtasten" mit dem herkömmlichen Timer-Interrupt ("Tick") approximieren . Es funktioniert jedoch möglicherweise nicht, wenn das Häkchen während der Leerlaufzeit ausgeschaltet wird, um Strom zu sparen
NO_HZ_IDLE
. Es kann auch fehlschlagen, wenn Sie zulassen, dass das Häkchen aus Leistungsgründen deaktiviert wird,NO_HZ_FULL
da dies gestartet werden mussVIRT_CPU_ACCOUNTING
. Die meisten Linux-Kernel verwenden die Energiesparfunktion. Einige eingebettete Systeme verwenden keine der beiden Funktionen. Hier ist meine Erklärung:Nach Abschluss der E / A sendet das Gerät einen Interrupt . Der Kernel-Interrupt-Handler weckt den Prozess mit
try_to_wake_up()
. Es subtrahiert einen vomnr_iowait
Zähler:Wenn der Prozess auf einer inaktiven CPU ausgelöst wird, ruft diese CPU auf
account_idle_time()
. Je nachdem , welche Konfiguration gilt, wird dies entweder genannt vontick_nohz_account_idle_ticks()
aus__tick_nohz_idle_restart_tick()
oder vonvtime_task_switch()
ausfinish_task_switch()
.Zu diesem Zeitpunkt
->nr_iowait
wurde bereits dekrementiert. Wenn es auf Null reduziert wird, wird keine iowait-Zeit aufgezeichnet.Dieser Effekt kann variieren: Es hängt davon ab, auf welcher CPU der Prozess geweckt wird. Wenn der Prozess auf derselben CPU geweckt wird, die den E / A-Abschluss-Interrupt empfangen hat, kann die Leerlaufzeit früher abgerechnet werden, bevor sie
->nr_iowait
dekrementiert wird. In meinem Fall stellte ich fest, dass CPU 0 den ahci- Interrupt behandelt, indem sie sich anschautewatch cat /proc/interrupts
.Ich habe dies mit einem einfachen sequentiellen Lesen getestet:
Wenn ich den Befehl mit CPU 0
taskset -c 0 ...
anhefte, sehe ich "richtige" Werte für iowait. Wenn ich es an eine andere CPU anhefte, sehe ich viel niedrigere Werte. Wenn ich den Befehl normal ausführe, hängt er vom Scheduler-Verhalten ab, das sich zwischen den Kernel-Versionen geändert hat. In neueren Kernels (4.17, 5.1, 5.2-rc5-ish) scheint der Befehl ungefähr 1/4 der Zeit für CPU 0 aufzuwenden, da die "iowait" -Zeit auf diesen Bruchteil reduziert ist.(Nicht erklärt: Warum das Ausführen dieses Tests auf meiner virtuellen Maschine jetzt für jede (oder jede) CPU "korrektes" iowait zu reproduzieren scheint. Ich vermute, dass dies damit zusammenhängt
IRQ_TIME_ACCOUNTING
, obwohl diese Funktion auch in meinen Tests außerhalb der VM verwendet wird.Ich habe auch nicht genau bestätigt, warum das Unterdrücken
NO_HZ_IDLE
"korrektes" iowait für jede CPU auf 4.17+ ergibt, aber nicht auf 4.16 oder 4.15.Das Ausführen dieses Tests auf meiner virtuellen Maschine scheint "korrektes" iowait für jede (oder jede) CPU zu reproduzieren. Das liegt an
IRQ_TIME_ACCOUNTING
. Es wird auch in Tests außerhalb der VM verwendet, aber beim Testen innerhalb der VM treten mehr Interrupts auf. Insbesondere gibt es mehr als 1000 "Funktionsaufruf-Interrupts" pro Sekunde auf der virtuellen CPU, auf der "dd" ausgeführt wird.Du solltest dich also nicht zu sehr auf die Details meiner Erklärung verlassen :-)
Ja.
Als ich das zum ersten Mal nachgeschlagen habe, fand ich die Rede von "Schluckauf". Das Problem wurde auch veranschaulicht, indem gezeigt wurde, dass die kumulative "iowait" -Zeit nicht monoton war. Das heißt, es ist manchmal rückwärts gesprungen (abgenommen). Es war nicht so einfach wie der Test oben.
Bei der Untersuchung stellten sie jedoch dasselbe grundlegende Problem fest. Eine Lösung wurde von Peter Zijlstra und Hidetoshi Seto vorgeschlagen und prototypisiert. Das Problem wird in der Titelmeldung erläutert:
[RFC PATCH 0/8] Überarbeitung iowait Buchhaltung (2014-07-07)
Ich habe darüber hinaus keine Hinweise auf Fortschritte gefunden. Zu einem der Details gab es eine offene Frage. Außerdem berührte die vollständige Serie spezifischen Code für die PowerPC-, S390- und IA64-CPU-Architekturen. Daher sage ich, dass es nicht trivial ist, dies zu beheben.
quelle
/proc/stat
, aber ich verwende/sys/devices/system/cpu/cpu*/cpuidle/state*/usage
und nach meinem besten Wissen waren immer genau (+ - ein paar%). Ich kann meine Tools nicht auf älteren Kerneln verwenden, da einige neue Informationen nicht vorhanden sind. Ich erwarte, dass test1 und test3 die gleichen Ergebnisse liefern, da das Häkchen im Ruhezustand 0 niemals stoppt./sys/devices/system/cpu/cpu*/cpuidle/state*/time
oben schreiben . Ich kann nur daran denken, den Kernel zu halbieren, einmal zwischen Kernel 4.15 und 4.16, dann wieder zwischen 4.16 und 4.17. Die zweite Halbierung könnte mit dem Wissen, das aus der ersten gewonnen wurde, schneller verlaufen. Ich habe gerade keine Zeit dafür, vielleicht in ein paar Tagen.4.15.0-1.fc28
und4.16.0-300.fc28
stimmen mit Ihren überein.taskset -c 0
v4.15 getestet ... Das Ausführen desdd
Befehls mittaskset -c 2
gibt das "richtige" iowait. Das Anheften an eine andere CPU gibt den "falschen" iowait. Und CPU2 ist wodd
landet es, wenn ich es nicht benutzetaskset
. (Früher habe ich dieatop
CPU-Iowait-Zeit gesehen). Ich schaue mir jedoch die zweite Halbierung an, um das aktuelle Verhalten zu erklären. Zu der Chance, dass es in der zweiten Änderung einen Kommentar dazu gegeben haben könnte.