Asumsi dasar saya adalah bahwa ketika faktor pembatas proses hanyalah disk dan CPU, maka total sistem "iowait" + penggunaan CPU harus sama dengan setidaknya 100% dari satu CPU logis. (Dalam kasus lain ini tidak akan berlaku. Misalnya ketika mengunduh file menggunakan wget
, jaringan sering menjadi faktor pembatas).
Asumsi ini dilanggar oleh tes sederhana. Apakah ini yang diharapkan? Jika itu diharapkan, apakah ada serangkaian kondisi di mana saya harus mengharapkan asumsi saya benar?
Ada beberapa latar belakang tentang "iowait" di sini: Bagaimana CPU mengetahui ada IO yang tertunda? Jawabannya di sini mengutip gagasan kontra-intuitif, bahwa iowait kumulatif "dapat menurun dalam kondisi tertentu". Saya ingin tahu apakah tes sederhana saya dapat memicu kondisi tidak berdokumen seperti itu?
UPDATE : Silakan lewati ke jawabannya .
Jawabannya memiliki tes yang lebih sederhana daripada yang saya gunakan pada awalnya. Saya menyimpan pertanyaan asli di bawah ini. Pertanyaan asli mungkin menunjukkan beberapa detail tambahan.
Pertanyaan asli
Dalam tes singkat, saya menggunakan dd
untuk meminta kernel untuk menghasilkan byte acak, dan menulisnya ke file. Saya menjalankan dd
perintah di dalam perf stat
, hanya untuk menghitung waktu CPU yang dihabiskan di dalam kernel. Saya juga menjalankannya di dalam perf trace -s
, untuk melaporkan waktu yang dihabiskan di dalam write()
. Pada saat yang sama, saya menjalankan vmstat 5
terminal lain, untuk melihat sistem "iowait".
- Saya berharap saya akan melihat setidaknya satu CPU sebagai "non-idle", yaitu 100% dari waktu itu berjalan, atau dihentikan tetapi menunggu IO ("iowait" state). Bukan itu.
- (Juga, saya mengharapkan untuk melihat waktu "iowait" kira-kira sama dengan waktu yang dihabiskan dalam menulis (). Tetapi tampaknya tidak melakukannya.)
Hasil terperinci dan lingkungan pengujian ditunjukkan di bawah ini. Juga ditunjukkan adalah tes alternatif, di mana asumsi saya memang berlaku. Catatan: itu perlu dijalankan perf stat
di dalam perf trace
, bukan sebaliknya. Ini dirinci di sini: Apakah "perf stat" (dan "time"!) Menunjukkan hasil yang salah ketika menjalankan "perf trace - s"?
Informasi latar belakang tentang "iowait"
Berikut ini adalah definisi yang diambil dari
sar
manual:% iowait:
Persentase waktu CPU atau CPU idle selama sistem memiliki permintaan I / O disk yang luar biasa.
Oleh karena itu,% iowait berarti bahwa dari sudut pandang CPU, tidak ada tugas yang dapat dijalankan, tetapi setidaknya satu I / O sedang berlangsung. iowait hanyalah bentuk waktu idle ketika tidak ada yang bisa dijadwalkan. Nilai tersebut mungkin atau mungkin tidak berguna dalam menunjukkan masalah kinerja, tetapi itu memberi tahu pengguna bahwa sistem idle dan bisa mengambil lebih banyak pekerjaan.
https://support.hpe.com/hpsc/doc/public/display?docId=c02783994
Ada juga artikel yang lebih panjang: Memahami I / O Tunggu (atau mengapa 0% Menganggur bisa OK) . Ini menjelaskan bagaimana Anda dapat melihat definisi dengan jelas dari kode kernel. Kodenya agak berubah, tetapi idenya masih jelas:
/*
* 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;
}
Artikel ini juga menunjukkan sejumlah percobaan terkait pada sistem CPU tunggal. Beberapa eksperimen bahkan digunakan dd
dengan if=/dev/urandom
! Namun percobaan tidak termasuk tes saya dd if=/dev/urandom of=test.out
. Itu hanya menggunakan dd if=/dev/urandom of=/dev/null
.
"IO wait" sedikit lebih sulit untuk dipikirkan sekarang karena kami menggunakan sistem multi-CPU, tapi saya pikir saya masih memahaminya, berdasarkan kode yang dikutip.
Lingkungan Hidup
Saya memiliki empat CPU logis.
Saya menggunakan LVM, dan sistem file ext4. Saya tidak menggunakan enkripsi apa pun pada disk atau sistem file saya. Saya tidak memiliki sistem file jaringan yang terpasang sama sekali, jadi saya tidak membaca atau menulis sistem file jaringan.
Hasil di bawah ini dari kernel 4.20.15-200.fc29.x86_64
, menggunakan noop
penjadwal IO. The cfq
IO scheduler juga memberikan hasil yang sama.
(Saya juga melihat hasil yang mirip pada kernel build yang didasarkan pada konfigurasi yang serupa, tetapi lebih dekat ke kernel versi 5.1, dan menggunakan mq-deadline
. Jadi itu menggunakan blk-mq
kode baru ).
Tes dan hasil
$ 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%
...
Saya membaca iowait
angka dari wa
kolom vmstat
. Anda dapat mengetahui kapan tes sedang berjalan dengan melihat io
kolom ( bo
= 1K blok output).
$ 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
Hasil tes di mana ia tahan (di dalam VM)
Saya mencoba tes yang sama di dalam VM dengan 1 CPU, yang menjalankan kernel 5.0.9-301.fc30.x86_64
dan menggunakan mq-deadline
(dan karenanya blk-mq). Dalam tes ini, berhasil seperti yang saya harapkan.
$ 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%
...
Output dari 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
Saya mencoba menambahkan CPU ke VM dan menguji lagi. Hasilnya variabel: kadang-kadang menunjukkan sekitar 0% di kolom idle, dan kadang-kadang menunjukkan sekitar 50% idle (yaitu satu dari dua CPU). Dalam kasus 0% "idle", "iowait" sangat tinggi yaitu lebih dari satu nilai CPU. Yaitu titik harapan saya 2 tidak benar. Aku begrudgingly bisa menerima ini batasan jelas dari "iowait" pada sistem multi-CPU. (Meskipun saya tidak begitu memahaminya. Jika seseorang ingin menjelaskannya dengan tepat, itu akan bagus). Namun, "idle" tidak di atas 50% dalam kedua kasus, jadi tes ini masih konsisten dengan asumsi pertama saya tentang "iowait".
Saya mencoba mematikan VM dan memulainya dengan 4 CPU. Demikian pula, sering saya memiliki idle 75%, dan kadang-kadang saya memiliki idle 50%, tetapi saya tidak melihat lebih dari 75% idle (yaitu lebih dari tiga dari empat CPU).
Sedangkan pada sistem fisik dengan 4 CPU, saya masih dapat mereproduksi hasil idle lebih dari 80% seperti yang ditunjukkan di atas.
this_rq()->nr_iowait
adalah jumlah tugas yang menunggu hanya menggunakanio_schedule()
CPU saat ini . Apakah aku salah?iowait
mencoba mengukur waktu yang dihabiskan menunggu I / O, secara umum. Ini tidak dilacak oleh CPU tertentu, juga tidak bisa" . Biarkan saya menekankan saya tidak yakin akan hal ini, hanya mengungkapkan keterkejutan.atop
, atauatopsar -c 5
, Anda akan melihat angka penggunaan per-cpu. Mereka termasuk iowait, dan angka-angka iowait per-CPU dapat menunjukkan nilai-nilai yang tidak nol :-). Atausar -P ALL 1
, jika Anda tidak menggunakannyaatop
. Ini adalah caraiowait
model diperluas untuk sistem multi-CPU ... Yang saya tidak jelas adalah apakah model ini sebenarnya dapat digunakan, atau apakah ini adalah cara yang memungkinkan kode iowait terus bekerja ketika hanya ada satu CPU online, tetapi tidak bisa dipercaya sebaliknya.Jawaban:
Pemberitahuan konten : posting ini mencakup tautan ke berbagai diskusi dan kode Linux. Beberapa konten tertaut tidak memenuhi Kode Etik saat ini untuk StackExchange atau untuk Linux . Sebagian besar mereka "menghina kode [tetapi bukan orangnya]". Namun beberapa bahasa digunakan, yang seharusnya tidak diulang. Saya meminta Anda untuk tidak meniru, membeo, atau memperdebatkan bahasa tersebut.
Saya menemukan masalah saya. Itu sudah diperhatikan lima tahun lalu, dan itu tidak akan sepele untuk diperbaiki.
Waktu "iowait" diperbarui oleh fungsi
account_idle_time()
:Ini berfungsi seperti yang saya harapkan, jika Anda mendekati waktu cpu dengan "sampling" dengan interupsi timer tradisional ("tick"). Namun, ini mungkin tidak berfungsi jika kutu dimatikan selama waktu siaga untuk menghemat daya -
NO_HZ_IDLE
. Mungkin juga gagal jika Anda membiarkan centang dimatikan karena alasan kinerja -NO_HZ_FULL
- karena itu memerlukan permulaanVIRT_CPU_ACCOUNTING
. Kebanyakan kernel Linux menggunakan fitur hemat daya. Beberapa sistem tertanam tidak menggunakan fitur mana pun. Inilah penjelasan saya:Ketika IO selesai, perangkat mengirim interupsi . Penangan interupsi kernel membangunkan proses menggunakan
try_to_wake_up()
. Itu mengurangi satu darinr_iowait
konter:Jika proses dibangunkan pada CPU yang tidak digunakan, CPU tersebut akan memanggil
account_idle_time()
. Bergantung pada konfigurasi mana yang berlaku, ini disebut daritick_nohz_account_idle_ticks()
dari__tick_nohz_idle_restart_tick()
, atau darivtime_task_switch()
darifinish_task_switch()
.Pada saat ini,
->nr_iowait
sudah dikurangi. Jika dikurangi menjadi nol, maka tidak ada waktu iowait akan direkam.Efek ini dapat bervariasi: tergantung pada proses mana CPU dibangunkan. Jika proses dibangunkan pada CPU yang sama yang menerima interupsi penyelesaian IO, waktu idle bisa dipertanggungjawabkan lebih awal, sebelum
->nr_iowait
dikurangi. Dalam kasus saya, saya menemukan CPU 0 menangani interupsi ahci , dengan melihatwatch cat /proc/interrupts
.Saya menguji ini dengan membaca sekuensial sederhana:
Jika saya menyematkan perintah ke CPU 0 menggunakan
taskset -c 0 ...
, saya melihat nilai "benar" untuk iowait. Jika saya menyematkannya ke CPU yang berbeda, saya melihat nilai yang jauh lebih rendah. Jika saya menjalankan perintah secara normal, ini bervariasi tergantung pada perilaku scheduler, yang telah berubah di antara versi kernel. Dalam kernel baru-baru ini (4.17, 5.1, 5.2-rc5-ish), perintah tersebut sepertinya menghabiskan sekitar 1/4 waktu pada CPU 0, karena waktu "iowait" dikurangi menjadi fraksi itu.(Tidak dijelaskan: mengapa menjalankan tes ini pada mesin virtual saya sekarang tampaknya mereproduksi iowait "benar", untuk setiap (atau apapun) CPU. Saya menduga ini mungkin melibatkan
IRQ_TIME_ACCOUNTING
, meskipun fitur ini juga digunakan dalam pengujian saya di luar VM.Saya juga belum mengkonfirmasi persis mengapa penekanan
NO_HZ_IDLE
memberi iowait "benar" untuk setiap CPU pada 4.17+, tetapi tidak pada 4.16 atau 4.15.Menjalankan tes ini pada mesin virtual saya sepertinya mereproduksi iowait "benar", untuk setiap (atau setiap) CPU. Ini karena
IRQ_TIME_ACCOUNTING
. Itu juga digunakan dalam tes di luar VM, tapi saya mendapatkan lebih banyak interupsi ketika menguji di dalam VM. Secara khusus, ada lebih dari 1000 "Function call interrupts" per detik pada CPU virtual yang dijalankan "dd".Jadi, Anda jangan terlalu mengandalkan detail penjelasan saya :-)
Iya.
Ketika saya pertama kali melihat ini, saya menemukan pembicaraan tentang "cegukan". Juga, masalahnya diilustrasikan dengan menunjukkan waktu kumulatif "iowait" adalah non-monotonik. Itu kadang-kadang melompat mundur (menurun). Itu tidak semudah tes di atas.
Namun, ketika mereka menyelidiki mereka menemukan masalah mendasar yang sama. Sebuah solusi diusulkan dan diujicobakan, oleh Peter Zijlstra dan Hidetoshi Seto. Masalahnya dijelaskan dalam pesan sampul:
[RFC PATCH 0/8] ulang akuntansi iowait (2014-07-07)
Saya tidak menemukan bukti kemajuan di luar ini. Ada pertanyaan terbuka di salah satu detailnya. Seri lengkap juga menyentuh kode spesifik untuk arsitektur CPU PowerPC, S390, dan IA64. Jadi saya katakan ini bukan hal sepele untuk diperbaiki.
sumber
/proc/stat
, tapi saya gunakan/sys/devices/system/cpu/cpu*/cpuidle/state*/usage
, dan sejauh pengetahuan saya selalu akurat (+ - beberapa%). Saya tidak dapat menggunakan alat saya pada kernel yang lebih lama karena beberapa informasi baru tidak ada. Perhatikan bahwa saya berharap test1 dan test3 memberikan hasil yang sama, karena/sys/devices/system/cpu/cpu*/cpuidle/state*/time
atas. Saya hanya bisa berpikir untuk membagi dua kernel, sekali untuk antara kernel 4.15 dan 4.16, sekali lagi antara 4.16 dan 4.17. Pembelahan kedua mungkin berjalan lebih cepat dengan pengetahuan yang diperoleh dari yang pertama. Saya tidak punya waktu untuk melakukannya sekarang, mungkin dalam beberapa hari.4.15.0-1.fc28
dan4.16.0-300.fc28
setuju dengan Anda.taskset -c 0
pada v4.15 ... Menjalankandd
perintah dengantaskset -c 2
memberikan iowait "benar". Menjepit ke CPU lain memberikan iowait "salah". Dan cpu2 adalah tempatdd
berakhirnya jika saya tidak menggunakantaskset
. (Saya duluatop
melihat waktu per-cpu iowait). Saya melihat pada pembagian kedua, untuk menjelaskan perilaku saat ini. Kemungkinan ada beberapa komentar tentang ini di perubahan kedua.