Moje podstawowe założenie dotyczące systemu „iowait” nie ma zastosowania

13

Moje podstawowe założenie jest takie, że gdy ograniczeniem procesu są tylko dysk i procesor, wówczas całkowite użycie systemu „iowait” + procesor powinno wynosić co najmniej 100% jednego logicznego procesora. (W innych przypadkach nie będzie to obowiązywać. Np. Przy pobieraniu pliku przy użyciu wgetsieć często stanowi czynnik ograniczający).

To założenie jest naruszane przez prosty test. Czy jest to oczekiwane? Jeśli się tego spodziewamy, czy istnieje zestaw warunków, w których powinienem oczekiwać, że moje założenie będzie prawdziwe?

Jest trochę tła na temat „iowait” tutaj: skąd procesor wie, że oczekuje na IO? Odpowiedź przytacza tu sprzeczną z intuicją ideę, że skumulowane iowait „może się zmniejszyć w pewnych warunkach”. Zastanawiam się, czy mój prosty test może wywołać taki nieudokumentowany warunek?

AKTUALIZACJA : Przejdź do odpowiedzi .

Odpowiedź ma prostszy test niż ten, którego użyłem pierwotnie. Zachowałem oryginalne pytanie poniżej. Oryginalne pytanie może zawierać dodatkowe informacje.

Oryginalne pytanie

W krótkim teście używam dddo żądania jądra do wygenerowania losowych bajtów i zapisania ich w pliku. Uruchamiam ddpolecenie wewnątrz perf stat, aby uzyskać czas procesora spędzony w jądrze. Wrzucam też do środka perf trace -s, żeby zgłosić czas spędzony w środku write(). W tym samym czasie uruchamiam vmstat 5inny terminal, aby zobaczyć system „iowait”.

  1. Spodziewałem się, że zobaczę co najmniej jeden cały procesor jako „bezczynny”, tj. 100% czasu albo jest uruchomiony, albo zatrzymany, ale czeka na IO (stan „iowait”). Nie było.
  2. (Poza tym spodziewałem się, że czas „iowait” będzie mniej więcej odpowiadał czasowi spędzanemu na pisaniu (). Ale nie wydaje się, żeby tak się działo.)

Szczegółowe wyniki i środowisko testowe pokazano poniżej. Pokazano również alternatywny test, w którym moje założenie się utrzymało. Uwaga: trzeba było biegać do perf statśrodka perf trace, a nie na odwrót. Jest to szczegółowo opisane tutaj: Czy „perf stat” (i „time”!) Pokazuje nieprawidłowe wyniki podczas uruchamiania „perf trace - s”?

Podstawowe informacje o „iowait”

Poniżej znajduje się definicja zaczerpnięta ze strony sarman:

% iowait:

Procent czasu bezczynności procesora lub procesorów, podczas którego system miał zaległe żądanie wejścia / wyjścia dysku.

Dlatego% iowait oznacza, że ​​z punktu widzenia CPU żadne zadania nie były uruchamialne, ale co najmniej jedno we / wy było w toku. iowait jest po prostu formą bezczynności, kiedy nic nie można zaplanować. Wartość może, ale nie musi, być użyteczna przy wskazywaniu problemu z wydajnością, ale informuje użytkownika, że ​​system jest bezczynny i mógł zająć więcej pracy.

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

Jest też dłuższy artykuł: Zrozumienie We / Wy Czekaj (lub dlaczego 0% Bezczynności może być OK) . To wyjaśnia, jak wyraźnie zobaczyć definicję z kodu jądra. Kod nieco się zmienił, ale pomysł jest nadal jasny:

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

Artykuł pokazuje również szereg powiązanych eksperymentów na systemie z jednym procesorem. Niektóre z tych eksperymentów nawet korzystać ddz if=/dev/urandom ! Jednak eksperymenty nie obejmują mojego testu dd if=/dev/urandom of=test.out . Używa tylko dd if=/dev/urandom of=/dev/null .

„IO wait” jest teraz nieco trudniejsze do przemyślenia, ponieważ używamy systemów wieloprocesorowych, ale myślę, że nadal to rozumiem, na podstawie cytowanego kodu.

Środowisko

Mam cztery logiczne procesory.

Używam LVM i systemu plików ext4. Nie używam żadnego szyfrowania na moim dysku lub systemie plików. W ogóle nie mam podłączonego sieciowego systemu plików, więc nie czytam ani nie piszę sieciowego systemu plików.

Poniższe wyniki pochodzą z jądra 4.20.15-200.fc29.x86_64, przy użyciu programu noopplanującego we / wy. Program cfqIO również daje podobne wyniki.

(Widziałem również podobne wyniki dla kompilacji jądra, która była oparta na podobnej konfiguracji, ale była bliższa wersji jądra 5.1 i korzystającej z niej mq-deadline. Więc to było przy użyciu nowego blk-mqkodu).

Test i wyniki

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

Przeczytałem iowaitrysunek z wakolumny vmstat. Możesz sprawdzić, kiedy test jest uruchomiony, patrząc na iokolumnę ( bo= 1K bloków wyjściowych).

$ 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

Wyniki testu tam, gdzie się trzyma (wewnątrz maszyny wirtualnej)

Próbowałem tego samego testu na maszynie wirtualnej z 1 procesorem, na którym działało jądro 5.0.9-301.fc30.x86_64i używałem mq-deadline(a więc i blk-mq). W tym teście działało tak, jak się tego spodziewałem.

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

Wyjście 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

Próbowałem dodać procesor na gorąco do maszyny wirtualnej i ponownie przetestować. Wyniki były zmienne: czasami wykazywało około 0% w bezczynnej kolumnie, a czasami wykazywało około 50% bezczynności (tj. Jeden z dwóch procesorów). W przypadku 0% „bezczynności”, „iowait” był bardzo wysoki, tj. Warty więcej niż jednego procesora. Tj. Moje oczekiwanie, punkt 2 nie był poprawny. Mogę niechętnie zaakceptować to oczywiste ograniczenie „iowait” w systemach wieloprocesorowych. (Chociaż nie do końca to rozumiem. Jeśli ktoś chciałby to dokładnie wyjaśnić, byłoby świetnie). Jednak „bezczynność” nie przekraczała 50% w obu przypadkach, więc testy te były nadal zgodne z moim pierwszym założeniem dotyczącym „iowait”.

Próbowałem wyłączyć maszynę wirtualną i uruchomić ją z 4 procesorami. Podobnie, często miałem dokładnie 75% bezczynności, a czasami miałem tak niskie, jak 50% bezczynności, ale nie widziałem więcej niż 75% bezczynności (tj. Więcej niż trzy z czterech procesorów).

Podczas gdy w systemie fizycznym z 4 procesorami nadal mogę odtworzyć wynik ponad 80% bezczynności, jak pokazano powyżej.

sourcejedi
źródło
Czy mógłbyś dodać trochę uwag do swoich dwóch oczekiwań? Czy możesz dodać, czy rzeczywista wartość była większa czy mniejsza od twoich oczekiwań. Rozumiem, że jest to w surowych danych, byłoby po prostu trochę bardziej czytelne. Nie jestem pewien, dlaczego spodziewasz się 1 procesora (100%). Na podstawie jednego z linków i cytowanego kodu jądra, pojedyncza operacja IO przełączy cały czas IDLE na czas IOWAIT (wszystkie 4 rdzenie - 400%).
Philip Couling
@PhilipCouling „Spodziewałem się, że zobaczę przynajmniej jeden cały procesor jako„ bezczynny ”... To nie było”. Czas bezczynności był wyższy niż oczekiwano, co obwiniam o to, że czas oczekiwania był niższy niż się spodziewałem. Myślę, że w kodzie jądra this_rq()->nr_iowaitjest liczba zadań, które czekają przy użyciu tylko io_schedule() na bieżącym procesorze . Czy się mylę?
sourcejedi
1
Nie jestem wcale pewien, ale wydaje mi się to zaskakujące. Ta niespodzianka zdaje się zgadzać z odpowiedzią Stephena Kitta, w której mówi on: iowaitogólnie próbuje zmierzyć czas spędzony na oczekiwaniu na operacje we / wy . Chciałbym podkreślić, że nie jestem tego pewien, tylko wyrażam zdziwienie.
Philip Couling
@ PhilipCouling, jeśli uruchomisz atop, lub atopsar -c 5zobaczysz dane dotyczące zużycia procesora. Obejmują one iowait, a liczby iowait na procesor mogą pokazywać różne niezerowe wartości :-). Lub sar -P ALL 1, jeśli nie używasz atop. W ten sposób iowaitmodel został rozszerzony dla systemów wieloprocesorowych ... Nie jestem pewien, czy ten model jest rzeczywiście użyteczny, czy też jest to sposób, który pozwala kodowi iowait kontynuować pracę, gdy jest tylko jeden procesor online, ale w przeciwnym razie nie jest to godne zaufania.
sourcejedi

Odpowiedzi:

7

Informacja o treści : ten post zawiera linki do różnych dyskusji na temat Linuksa i kodu. Niektóre połączone treści nie spełniają obecnego Kodeksu postępowania StackExchange lub Linux . Przeważnie „obrażają kod [ale nie osobę]”. Jednak używany jest jakiś język, którego po prostu nie należy powtarzać. Proszę, abyście unikali naśladowania, papugowania lub debatowania nad takim językiem.


Re: iowait vs bezczynne rozliczanie jest „niespójne” - iowait jest zbyt niskie

W dniu 05.07.2019 12:38 Peter Zijlstra napisał:

W piątek, 05 lipca 2019 o 12:25:46 +0100, Alan Jenkins napisał:

Mój czas „iowait” procesora wydaje się być zgłaszany niepoprawnie. Czy wiesz, dlaczego tak się stało?

Ponieważ iowait to magiczna liczba losowa, która nie ma rozsądnego znaczenia. Osobiście wolałbym po prostu usunąć całość, z wyjątkiem ABI : /

Zobacz także komentarz w pobliżu nr_iowait ()

Dzięki. Traktuję [problemy wymienione w bieżącej dokumentacji] jako różne problemy, ale masz na myśli, że nie ma dużego zapotrzebowania (lub punktu) na „naprawienie” mojego problemu.

Znalazłem mój problem. Zostało to zauważone już pięć lat temu i naprawienie go nie byłoby trywialne.

Czas „iowait” jest aktualizowany przez funkcję 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;
}

Działa to tak, jak się spodziewałem, jeśli zbliżasz czas procesora przez „próbkowanie” z tradycyjnym przerwaniem timera („tik”). Jednak może nie działać, jeśli tykanie jest wyłączone w czasie bezczynności w celu oszczędzania energii - NO_HZ_IDLE. Może się również nie powieść, jeśli zezwolisz na wyłączenie kleszcza ze względu na wydajność - NO_HZ_FULL- ponieważ wymaga to uruchomienia VIRT_CPU_ACCOUNTING. Większość jąder Linuksa korzysta z funkcji oszczędzania energii. Niektóre systemy wbudowane nie używają żadnej z tych funkcji. Oto moje wyjaśnienie:

Po zakończeniu operacji we / wy urządzenie wysyła przerwanie . Program obsługi przerwań jądra budzi proces przy użyciu try_to_wake_up(). Odejmuje jeden od nr_iowaitlicznika:

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

Jeśli proces zostanie przebudzony na bezczynnym procesorze, ten procesor wywołuje account_idle_time(). W zależności od tego, która konfiguracja ma zastosowanie, jest to wywoływane tick_nohz_account_idle_ticks()z __tick_nohz_idle_restart_tick()lub vtime_task_switch()z finish_task_switch().

Do tego czasu ->nr_iowaitzostał już zmniejszony. Jeśli zostanie zredukowany do zera, nie będzie rejestrowany żaden czas oczekiwania.

Ten efekt może się różnić: zależy od procesora, na którym proces jest budzony. Jeśli proces zostanie przebudzony na tym samym CPU, który odebrał przerwanie zakończenia We / Wy, czas bezczynności może zostać rozliczony wcześniej, zanim ->nr_iowaitzostanie zmniejszony. W moim przypadku znalazłem, że CPU 0 obsługuje przerwanie ahci , patrząc na watch cat /proc/interrupts.

Przetestowałem to za pomocą prostego sekwencyjnego odczytu:

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

Jeśli taskset -c 0 ...przypiszę polecenie do CPU 0 za pomocą , zobaczę „poprawne” wartości dla iowait. Po przypięciu go do innego procesora widzę znacznie niższe wartości. Jeśli uruchomię polecenie normalnie, różni się ono w zależności od zachowania programu planującego, który zmienił się między wersjami jądra. W najnowszych jądrach (4.17, 5.1, 5.2-rc5-ish) polecenie wydaje się spędzać około 1/4 czasu na CPU 0, ponieważ czas „iowait” jest zredukowany do tej części.

(Nie wyjaśniono: dlaczego uruchomienie tego testu na mojej maszynie wirtualnej wydaje się teraz odtwarzać „poprawne” iowait dla każdego (lub dowolnego) procesora. Podejrzewam, że może to dotyczyć IRQ_TIME_ACCOUNTING, chociaż ta funkcja jest również używana w moich testach poza maszyną wirtualną.

Nie potwierdziłem również dokładnie, dlaczego tłumienie NO_HZ_IDLEdaje „poprawne” iowait dla każdego procesora w wersji 4.17+, ale nie w wersji 4.16 lub 4.15.

Uruchomienie tego testu na mojej maszynie wirtualnej wydaje się odtwarzać „poprawny” iowait dla każdego (lub dowolnego) procesora. Wynika to z IRQ_TIME_ACCOUNTING. Jest również używany w testach poza maszyną wirtualną, ale dostaję więcej przerwań podczas testowania wewnątrz maszyny wirtualnej. W szczególności na wirtualnym procesorze, na którym działa „dd”, jest ponad 1000 „przerwań wywołania funkcji” na sekundę.

Więc nie powinieneś zbytnio polegać na szczegółach mojego wyjaśnienia :-)

Jest trochę tła na temat „iowait” tutaj: skąd procesor wie, że oczekuje na IO? Odpowiedź przytacza tu sprzeczną z intuicją ideę, że skumulowane iowait „może się zmniejszyć w pewnych warunkach”. Zastanawiam się, czy mój prosty test może wywołać taki nieudokumentowany warunek?

Tak.

Kiedy po raz pierwszy to sprawdziłem, znalazłem wzmiankę o „czkawce”. Problem zilustrowano również pokazując, że skumulowany czas „iowait” był niemonotoniczny. Oznacza to, że czasami skakał do tyłu (zmniejszony). To nie było tak proste jak powyższy test.

Jednak podczas dochodzenia odkryli ten sam podstawowy problem. Rozwiązanie zostało zaproponowane i prototypowane odpowiednio przez Petera Zijlstrę i Hidetoshi Seto. Problem wyjaśniono w komunikacie przewodnim:

[RFC PATCH 0/8] przeróbka księgowości iowait (2014-07-07)

Nie znalazłem żadnych dowodów postępu poza tym. Pytanie dotyczyło jednego z szczegółów. Ponadto cała seria dotknęła konkretnego kodu dla architektury procesorów PowerPC, S390 i IA64. Mówię więc, że nie jest to łatwe do naprawienia.

sourcejedi
źródło
2
Czy możesz potwierdzić lub odrzucić (używając vmstat): Jądro 4.15 robi to, czego oczekujesz, niezależnie od włączonych lub wyłączonych stanów bezczynności; Jądro 4.16 nie robi tego, czego oczekujesz. vmstat wydaje się używać /proc/stat, ale używam /sys/devices/system/cpu/cpu*/cpuidle/state*/usagei, zgodnie z moją najlepszą wiedzą, zawsze były dokładne (+ - kilka%). Nie mogę używać moich narzędzi na starszych jądrach, ponieważ nie ma tam niektórych nowych informacji. Zauważ, że oczekuję, że test1 i test3 dadzą te same wyniki, ponieważ tyknięcie nigdy nie zatrzymuje się w stanie bezczynności 0.
Doug Smythies
1
Chciałem napisać /sys/devices/system/cpu/cpu*/cpuidle/state*/timepowyżej. Mogę tylko rozdzielić jądro, raz pomiędzy jądrem 4.15 a 4.16, potem znowu między 4.16 a 4.17. Druga bisekcja może iść szybciej dzięki wiedzy zdobytej od pierwszej. Nie mam czasu, żeby to zrobić teraz, może za kilka dni.
Doug Smythies
1
@DougSmythies dziękuję! Twoje testy działają tak samo jak moje oryginalne. Moje wyniki 4.15.0-1.fc28i 4.16.0-300.fc28zgadzam się z twoimi.
sourcejedi
OK Myślę, że jestem gotowy na odpowiedź z listy linux-pm. Mam nadzieję, że ktoś będzie miał wgląd i będziemy mogli uniknąć podziału jądra.
Doug Smythies,
1
@DougSmythies wtf. pierwsza bisekcja (4.15–4.16) daje github.com/torvalds/linux/commit/806486c377e3 „harmonogram / fair: Nie migruj, jeśli prev_cpu jest bezczynny”. Więc przetestowałem taskset -c 0na wersji 4.15 ... Uruchomienie ddpolecenia z taskset -c 2daje „poprawne” iowait. Przypięcie do dowolnego innego procesora daje „zły” iowait. A cpu2 jest tym, co ddkończy się, jeśli nie używam taskset. (Kiedyś atopwidziałem czas iowait na procesor). Spoglądam jednak na drugą bisekcję, aby wyjaśnić obecne zachowanie. Przy okazji może być jakiś komentarz na ten temat w drugiej zmianie.
sourcejedi