Моє основне припущення щодо системи "iowait" не відповідає


13

Моє основне припущення полягає в тому, що коли лише обмежуючими факторами процесу є диск і процесор, то загальна система "iowait" + використання процесора повинна дорівнювати щонайменше 100% одного логічного процесора. (В інших випадках це не буде wgetдотримано . Наприклад, при завантаженні файлу за допомогою мережі часто є обмежуючим фактором).

Це припущення порушено простим тестом. Це очікується? Якщо очікується, чи існує ряд умов, коли я повинен очікувати, що моє припущення справдиться?

Тут є деякі відомості про "iowait": Як CPU знає, що IO очікує на розгляд? Тут відповідь цитує контр-інтуїтивну думку, що накопичувальне іоїти "може зменшитися за певних умов". Цікаво, чи може мій простий тест викликати такий незадокументований стан?

ОНОВЛЕННЯ : Перейдіть до відповіді .

У відповіді є більш простий тест, ніж той, який я використовував спочатку. Я зберегла початкове запитання нижче. Оригінальне запитання може містити деякі додаткові деталі.

Оригінальне запитання

У короткому тесті я використовую ddзапит ядра на створення випадкових байтів і записую їх у файл. Я запускаю ddкоманду всередині perf stat, просто щоб отримати підрахунок часу процесора, проведеного всередині ядра. Я також запускаю його всередину perf trace -s, щоб повідомити про час, проведений всередині write(). Одночасно біжу vmstat 5в інший термінал, щоб побачити систему "iowait".

  1. Я очікував, що побачу хоча б один цілий процесор як "непрацюючий", тобто 100% часу він або працює, або зупиняється, але чекає IO (стан "iowait"). Це не так.
  2. (Також я очікував, що час "iowait" приблизно відповідає часу, витраченому на запис (). Але, схоже, це не робилося.)

Детальні результати та середовище тестування показані нижче. Також показаний альтернативний тест, де моє припущення справдилося. Примітка: потрібно було бігати perf statвсередину perf trace, а не навпаки. Це детально тут: Чи показує "perf stat" (та "time"!) Неправильні результати при запуску "perf trace - s"?

Довідкова інформація про "iowait"

Далі йдеться з визначення, взятого на сторінці сторінки sar:

% iowait:

Відсоток часу, коли ЦП або ЦП були непрацюючими, протягом яких система мала непогашений запит вводу / виводу диска.

Тому% iowait означає, що з точки зору процесора жодні завдання не можна було виконати, але принаймні один введення / виведення виконується. iowait - це просто форма простою, коли нічого не можна було запланувати. Значення може бути або не корисним для вказівки на проблеми з продуктивністю, але воно вказує користувачеві, що система в режимі очікування та може зайняти більше роботи.

https://support.hpe.com/hpsc/doc/public/display?docId=c02783994

Є ще довша стаття: Розуміння очікування вводу / виводу (або чому 0% простою може бути в порядку) . Це пояснює, як ви можете чітко бачити визначення з коду ядра. Код дещо змінився, але ідея все одно зрозуміла:

/*
 * 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;
}

У статті також показаний ряд пов'язаних експериментів над системою з одним процесором. Деякі з експериментів , навіть використовувати ddз if=/dev/urandom ! Однак експерименти не включають мій тест dd if=/dev/urandom of=test.out . Він тільки використовує dd if=/dev/urandom of=/dev/null .

"IO wait" трохи складніше думати про це зараз, оскільки ми використовуємо багатопроцесорні системи, але, думаю, я все-таки це розумію, виходячи з цитованого коду.

Навколишнє середовище

У мене є чотири логічні процесори.

Я використовую LVM та файлову систему ext4. Я не використовую жодного шифрування на своєму диску чи файловій системі. У мене взагалі не встановлена ​​мережева файлова система, тому я не читаю і не записую мережеву файлову систему.

Нижче наведені результати з ядра 4.20.15-200.fc29.x86_64, використовуючи noopпланувальник IO. cfqПланувальник введення - виведення також дає аналогічні результати.

(Я також бачив подібні результати по збірці ядра, яка базувалася на подібній конфігурації, але була ближчою до версії ядра 5.1 та використанню mq-deadline. Отже, використовувався новий blk-mqкод).

Тест та результати

$ 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%
...

Я читав iowaitрисунок із waстовпця vmstat. Ви можете сказати, коли тест працює, переглянувши ioстовпчик ( bo= 1 К блоків виводу).

$ 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

Результати тестування там, де він утримується (всередині VM)

Я спробував той самий тест всередині VM з 1 процесором, на якому було запущено ядро 5.0.9-301.fc30.x86_64і використовувалося mq-deadline(а значить, blk-mq). У цьому тесті він працював так, як я цього очікував.

$ 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%
...

Вихід 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

Я спробував гаряче додати процесор до VM і тестувати ще раз. Результати були мінливими: іноді він показував близько 0% у режимі очікування, а іноді - близько 50% в режимі очікування (тобто один із двох процесорів). У випадку 0% "простою", "iowait" був дуже високим, тобто вартості більше одного процесора. Тобто, моє сподівання, пункт 2 був невірним. Я можу з грубою думкою прийняти це очевидне обмеження "iowait" на багатопроцесорних системах. (Хоча я не зовсім це розумію. Якщо хтось хоче це точно пояснити, це було б чудово). Однак в будь-якому випадку "простої" не перевищували 50%, тому ці тести все ще відповідали моєму першому припущенню про "iowait".

Я спробував вимкнути VM і запустити його з 4 процесорів. Аналогічно, часто я мав рівно 75% холостого ходу, а іноді я мав менше 50% в режимі очікування, але я не бачив більше 75% в режимі очікування (тобто більше трьох з чотирьох процесорів).

Якщо у фізичній системі з 4 процесорами я все одно можу відтворити результат більш ніж 80% в режимі очікування, як показано вище.


Не хотіли б трохи зауважити свої два очікування. Чи можете ви додати, чи справжня вартість була більшою чи меншою, ніж ваші очікування. Я розумію, це є в необроблених даних, це було б просто трохи читабельніше. Мені трохи незрозуміло, чому ви очікуєте 1 процесор (100%). На основі одного з ваших посилань та коду ядра, який ви цитуєте, одна операція вводу-виводу переключить весь час IDLE на час IOWAIT (усі 4 ядра - 400%).
Філіп

@PhilipCouling "Я очікував, що побачу принаймні один цілий процесор як" непрацюючий "... Це не було". Час простою був більшим, ніж очікувалося, що я звинувачую в тому, що час в очікуванні був меншим, ніж я очікував. У коді ядра я думаю, що this_rq()->nr_iowaitце кількість завдань, які чекають на використання io_schedule() лише в поточному процесорі . Я помиляюся?
sourcejedi

1
Я зовсім не впевнений, але мені здається, що це дивно. Цей сюрприз, можливо, відповідає відповіді Стівена Кітта, де він каже: " iowaitнамагається взагалі виміряти час, витрачений на очікування вводу-виводу. Це не відстежується конкретним процесором, і не може бути" . Дозвольте наголосити, що я не впевнений у цьому, просто висловлюючи здивування.
Філіп

@PhilipCouling, якщо ви запускаєте atop, або atopsar -c 5ви побачите цифри використання кожного процесора. Вони включають iowait, і показники iowait на CPU можуть показувати різні, ненульові значення :-). Або sar -P ALL 1, якщо ви не використовуєте atop. Це так, як iowaitмодель була розширена для багатопроцесорних систем ... Чого мені незрозуміло, чи є ця модель насправді придатною для використання, чи це спосіб, який дозволяє коду iowait продовжувати працювати, коли є лише один процесор? он-лайн, але інакше просто не заслуговує на довіру.
sourcejedi

Відповіді:


7

Повідомлення про вміст : ця публікація містить посилання на різні дискусії та код Linux. Частина пов'язаного вмісту не відповідає чинному Кодексу поведінки для StackExchange або для Linux . Здебільшого вони "ображають код [, але не особу]". Однак використовується деяка мова, яка просто не повинна повторюватися. Я прошу вас уникати наслідування, попугайства чи дискусії про таку мову.


Re: Iowait проти простою облік "непослідовний" - iowait занадто низький

05.07.2019 12:38 Петро Зільстра написав:

У п'ятницю, 05 липня 2019 року о 12:25:46 +0100, Алан Дженкінс написав:

Можливо, час мого процесора "iowait" повідомляється неправильно. Чи знаєте ви, чому це могло статися?

Тому що iowait - це магічне випадкове число, яке не має здорового значення. Особисто я вважаю за краще просто видалити все, крім ABI : /

Також дивіться коментар біля nr_iowait ()

Дякую. Я вважаю [проблеми, згадані в поточній документації], як різні проблеми, але ви маєте на увазі, що не дуже багато попиту (або точки), щоб "виправити" мою проблему.

Я знайшов свою проблему. Це вже було помічено п'ять років тому, і це було б не банально.

Час "iowait" оновлюється функцією account_idle_time():

/*
 * 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;
}

Це працює, як я і очікував, якщо ви наближаєте час процесора за допомогою "вибірки" з традиційним перериванням таймера ("галочка"). Однак це може не спрацювати, якщо галочка відключена під час простою для економії електроенергії NO_HZ_IDLE. Він також може вийти з ладу, якщо ви дозволите відключити галочку з міркувань продуктивності - NO_HZ_FULL- тому що це вимагає запуску VIRT_CPU_ACCOUNTING. Більшість ядер Linux використовують функцію економії енергії. Деякі вбудовані системи не використовують жодної функції. Ось моє пояснення:

Коли IO завершено, пристрій надсилає переривання . Обробник переривання ядра прокидає процес за допомогою try_to_wake_up(). Він віднімає один із nr_iowaitлічильника:

if (p->in_iowait) {
    delayacct_blkio_end(p);
    atomic_dec(&task_rq(p)->nr_iowait);
}

Якщо процес прокидається в режимі очікування, цей процесор дзвонить account_idle_time(). Залежно від того, яка конфігурація застосовується, це викликається або tick_nohz_account_idle_ticks()з __tick_nohz_idle_restart_tick(), або vtime_task_switch()з finish_task_switch().

До цього часу ->nr_iowaitвже зменшено. Якщо вона зведена до нуля, тоді не буде записано жодного часу очікування.

Цей ефект може змінюватись: залежить від того, на якому процесорі процес буде пробуджений. Якщо процес буде пробуджений на тому ж процесорі, який отримав переривання завершення вводу-виводу, час очікування може бути враховано раніше, перш ніж ->nr_iowaitзменшиться. У моєму випадку я виявив, що процесор 0 обробляє перерви ahci , переглядаючи watch cat /proc/interrupts.

Я перевірив це простим послідовним читанням:

dd if=largefile iflag=direct bs=1M of=/dev/null

Якщо я taskset -c 0 ...зафіксую команду CPU 0 за допомогою , я побачу "правильні" значення для iowait. Якщо я закріплюю його на іншому процесорі, я бачу набагато менші значення. Якщо я запускаю команду нормально, вона змінюється в залежності від поведінки планувальника, яка змінилася між версіями ядра. В останніх ядрах (4.17, 5.1, 5.2-rc5-ish) команда, як видається, витрачає приблизно 1/4 часу на CPU 0, оскільки час "iowait" скорочується до цієї частки.

(Не пояснено: чому зараз тест на моїй віртуальній машині, здається, відтворює "правильну" iowait, для кожного (або будь-якого) процесора. Я підозрюю, що це може бути пов'язано IRQ_TIME_ACCOUNTING, хоча ця функція також використовується в моїх тестах поза VM.

Я також не підтвердив, чому придушення NO_HZ_IDLEдає "правильне" iooit для кожного процесора на 4.17+, але не на 4.16 або 4.15.

Запуск цього тесту на моїй віртуальній машині, схоже, відтворює "правильну" iowait для кожного (або будь-якого) процесора. Це пов’язано з IRQ_TIME_ACCOUNTING. Він також використовується в тестах поза VM, але я отримую більше перерв при тестуванні всередині VM. Зокрема, у віртуальному процесорі, на якому працює "dd", більше 1000 "переривань функціонального виклику" в секунду.

Тож не варто занадто покладатися на деталі мого пояснення :-)

Тут є деякі відомості про "iowait": Як CPU знає, що IO очікує на розгляд? Тут відповідь цитує контр-інтуїтивну думку, що накопичувальне іоїти "може зменшитися за певних умов". Цікаво, чи може мій простий тест викликати такий незадокументований стан?

Так.

Коли я вперше подивився на це, я знайшов розмову про «гикавку». Також проблему проілюстрували, показавши, що сукупний час «іоїти» був не монотонним. Тобто іноді стрибає назад (зменшується). Це було не так просто, як тест вище.

Однак, коли вони досліджували, вони виявили ту саму фундаментальну проблему. Рішення було запропоновано та прообразовано Петром Зільстра та Хідетосі Сето відповідно. Проблема пояснюється у супровідному повідомленні:

[RFC PATCH 0/8] переробляє обліковий запис iowait (2014-07-07)

Я не знайшов жодних доказів прогресу поза цим. З однієї деталі виникло відкрите запитання. Також повна серія торкнулася конкретного коду для архітектур процесорів PowerPC, S390 та IA64. Тому я кажу, що це не банально.


2
Чи можете ви підтвердити чи заперечити (використовуючи vmstat): Ядро 4.15 робить те, що ви очікуєте, незалежно від увімкнених або вимкнених режимів очікування; Ядро 4.16 не робить те, що ви очікуєте, незалежно. vmstat, здається, використовує /proc/stat, але я використовую /sys/devices/system/cpu/cpu*/cpuidle/state*/usage, і наскільки мені відомо, завжди був точним (+ - пара%). Я не можу використовувати свої інструменти на старих ядрах, оскільки нової інформації там немає. Зауважте, що я очікую, що test1 і test3 дадуть однакові результати, тому що галочка ніколи не зупиняється в режимі очікування 0.
Doug Smythies

1
Я мав намір написати /sys/devices/system/cpu/cpu*/cpuidle/state*/timeвище. Я можу подумати, щоб розділити ядро, раз для між ядром 4.15 і 4.16, потім знову між 4.16 і 4.17. Друга бісекція може пройти швидше, коли знання отримані від першого. Я не встигаю це зробити просто зараз, можливо, через кілька днів.
Doug Smythies

1
@DougSmythies дякую! Ваші тести працюють так само добре, як і мої оригінальні. Мої результати для 4.15.0-1.fc28і 4.16.0-300.fc28згодні з вашими.
sourcejedi

Гаразд, я думаю, що я готовий до відповіді списку linux-pm. Сподіваємось, хтось матиме деяке розуміння, і ми можемо уникнути поділу ядра.
Doug Smythies

1
@DougSmythies wtf. Перша бісекція (4.15-4.16) дає github.com/torvalds/linux/commit/806486c377e3 " sched / fair: Не мігруйте, якщо prev_cpu не працює". Тому я перевірив taskset -c 0на v4.15 ... Запуск ddкоманди з taskset -c 2дає "правильний" iowait. Прив’язка до будь-якого іншого процесора дає "неправильну" іоваіту. І cpu2 - це те, де ddя не використовую taskset. (Раніше я atopбачив час в режимі очікування). Хоча я дивлюся на другий поділ, щоб пояснити поточну поведінку. На ймовірність, що, можливо, були б коментарі щодо цього у другій зміні.
sourcejedi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.