Dlaczego gzip działa powoli, mimo że wydajność procesora i dysku twardego nie jest maksymalna?

14

Mam kilka plików JSON, po 20 GB każdy, które chcę skompresować gzip:

gzip file1.json

To zajmuje jeden pełny rdzeń procesora, wszystko w porządku.

Przetwarza około 25 MB / s (zapisano atop), mój dysk twardy może odczytać 125 MB / s, a ja mam 3 wolne rdzenie procesora, więc oczekuję przyspieszenia podczas kompresji wielu plików równolegle. Więc działam w innych terminalach:

gzip file2.json
gzip file3.json
gzip file4.json

O dziwo, moja przepustowość się nie zwiększa; Procesor ma około 25% na każdy rdzeń, a mój HD wciąż czyta tylko z prędkością 25 MB / s.

Dlaczego i jak to rozwiązać?

nh2
źródło

Odpowiedzi:

17

Dowiedziałem się:

Powodem jest to, że obecnie gzipdziała (pod względem szybkości procesora w porównaniu do prędkości wyszukiwania HD) na bardzo małych rozmiarach buforów .

Odczytuje kilka KB z pliku wejściowego, kompresuje go i opróżnia do pliku wyjściowego. Biorąc pod uwagę fakt, że wymaga to wyszukiwania dysku twardego, można wykonać tylko kilka operacji na sekundę.

Powodem, dla którego mój występ się nie skalował, jest to, że już gzipszukałem jak szalony.


Obejrzałem to za pomocą buffernarzędzia unix :

buffer -s 100000 -m 10000000 -p 100 < file1.json | gzip > file1.json.gz

Buforując dużo danych wejściowych przed wysłaniem ich do gzip, można znacznie zmniejszyć liczbę małych wyszukiwań. Opcje:

  • -si -mmają określić rozmiar bufora (uważam , że jest w KB, ale nie jestem pewien)
  • -p 100 upewnia się, że dane są przekazywane do gzip dopiero po wypełnieniu bufora w 100%

Działając cztery z nich równolegle, mogłem uzyskać przepustowość 4 * 25 MB / s, zgodnie z oczekiwaniami.


Nadal zastanawiam się, dlaczego gzip nie pozwala na zwiększenie rozmiaru bufora - w ten sposób jest całkiem bezużyteczny, jeśli działa na wirującym dysku.

EDYCJA : Wypróbowałem jeszcze kilka zachowań programów do kompresji:

  • bzip2 przetwarza tylko 2 MB / s ze względu na silniejszą / bardziej intensywną kompresję procesora
  • lzop wydaje się zezwalać na większe bufory: 70 MB / s na rdzeń, a 2 rdzenie mogą maksymalnie powiększyć mój HD bez nadmiernego przeszukiwania
nh2
źródło
Czy możesz ddzrobić to samo?
Simon Kuang
@SimonKuang Podejrzewam, że ddmożna zrobić to samo z jego bs=opcją, tak.
nh2
Brzmi jak ciekawy zbieg okoliczności, że w przypadku jednego pliku rozmiar bloku w pełni wykorzystywał zarówno pojedynczy rdzeń procesora, jak i IOPS napędu.
Dave L.
3

Po przejrzeniu pierwszych pięciu wykładów w MIT OpenCourseware dla 6.172: „Inżynieria wydajności systemów oprogramowania”, uruchomiłem analizator wydajności systemu Linux „perf” na umiarkowanie dużym pliku testowym. Wynik wydaje się pokazywać przeciągnięcia rurociągu, w których jedna instrukcja musi czekać na wynik poprzedniej.

       │         while (lookahead != 0) {                                                                
       │             /* Insert the string window[strstart .. strstart+2] in the                          
       │              * dictionary, and set hash_head to the head of the hash chain:                     
       │              */                                                                                 
       │             INSERT_STRING(strstart, hash_head);                                                 
  2.07 │       movzbl 0x8096d82(%edx),%eax                                                               
  3.99 │       mov    %edx,%ebp                                                                          
       │       shl    $0x5,%ecx                                                                          
  0.03 │       and    $0x7fff,%ebp                                                                       
  1.94 │       xor    %ecx,%eax                                                                          
  1.43 │       and    $0x7fff,%eax                                                                       
  2.01 │       mov    %eax,0x805e588                                                                     
  2.40 │       add    $0x8000,%eax                                                                      
  0.88 │       movzwl 0x8062140(%eax,%eax,1),%ecx                                                        
 23.79 │       movzwl %cx,%edi                                                                           
       │             /* Find the longest match, discarding those <= prev_length.  

Druga ostatnia instrukcja jest kopiowana do, %ecxa ostatnia musi poczekać (zablokowanie potoku), aż %cxrejestr będzie gotowy do użycia danych. To przeciągnięcie rurociągu utrzymuje pętlę zawierającą.

Wynika to z bardzo niejasnego stylu programowania „oldschoolowego” C.

użytkownik1295785
źródło
1

Wskazówka, która może zabrać go na jeszcze wyższy poziom prędkości w procesorze wielordzeniowym / hyperthreadingu:
(przy założeniu Ubuntu)

sudo apt-get zainstaluj moreutils

moreutils zawiera między innymi „GNU równoległy” - który ma wiele opcji pomagających w większym wykorzystaniu twojego procesora.

Hannu
źródło