Narzędzia do pakowania danych w systemach Linux takie jak gzip, xz, bzip2, brotli stosują kilka głównych algorytmów do kompresji danych oraz mnóstwo wariacji i ulepszeń, które mają za zadnie zwiekszyć stopień kompresji albo skrócić czas działania. Wspólną cechą tych narzędzi jest to, że działają jednowątkowo, wykorzystując jedne rdzeń procesora. Powoduje to, że chcąc przyspieszyć działania takich programów poprze możliwości sprzętowe, możemy jedynie zwiększać taktowanie procesora aby to uzyskać.
Istnieje jednak zupełnie inne rozwiązanie. Kompresję można przyspieszyć poprzez podzielenie danych na mniejsze pakiety i rozdzielenie zadań kompresji na kilka rdzeni procesora, tak aby wykonały się równolegle. W teorii pozwala to przyspaieszyć pakowanie nawet tyle razy ile mamy do dyspozycji rdzeni procesora. Biorąc pod uwagę, że obecnie (styczeń 2026) nawet w laptopach dostępne jest 4-8 rdzeni, a w stacjach roboczych czy serwerach możemy mieć od 16 do ponad 100 rdzeni, to warto zastanowić się nad takim rozwiązaniem, szczególnie gdy mamy do pakowania większe zbiory danych.
Jednym z narzędzi które oferuje wielowątkową równoległą kompresję danych
jest polecenie pigz.
pigz można opisać jako wielowątkową wersję programu
gzip. pigz używa
tego samego algorytmu - Deflate - co gzip oraz generuje pliki w formacie
.gz w pełnie zgodne z gzip. Działa poprzez podzielenie danych
wejściowych na bloki po 128 kB (lub o innym, zadanym rozmiarze), kompresję
ich w wielu wątkach, a następnie odpowienio je oznacza i szereguje w pliku
wyjściowym.
pigz może używać zadanej liczby wątków lub też wykrywać automatycznie
dostępne rdzenie (procesory).
pigz stosuje podobne opcje i przełączniki wiersza poleceń jak gzip, tzn
można wybrać zadany stopień kompresji (-#). Standarowa jest opcja rozpakowania
(-d) oraz opcje zachowania czasów modyfikacji plików.
Domyślne działanie z plikiekm jest też takie jak w gzip - tworzony jest
plik spakowany z rozszerzeniem .gz, a plik wejściowy jest usuwany.
Główna różnica w stosunku do "tradycyjnego" gzipa to opcja -p pozwalająca
wskazać ile rdzeni program ma użyć do pracy z kompresją. Domyślne
zachowanie to użycie wszystkich dostępnych rdzeni, lub 8 wątków, jeśli nie
można wykryć rdzeni. Wartość -p 1 wyłącza kompresję wielowątkową.
Przykład:
$ pigz -p 12 bigdata
pigz wykona kompresję z użyciem 8 rdzeni.
Przyjrzymy się jak działa przyspieszenie wielowątkowe w pigz. Wykonajmy
test polegajcy na kompresji tych samych danych z różną liczbą wątków.
Maszyny, system operacyjny i wersje pigz użyte w tym teście:
Test przeprowadzimy na zbiorze danych z projektu Prague Corpus Jest to archiwum tar (niskompresowane) zawierające około 30 plików różnych rodzajów (m.in. dźwięk, obraz, dokumenty, tekst, kody źródłowe, tabele danych) Maszyna, na której testowano:
- CPU: 2 x 24-Core Intel Xeon Platinum 8268
- RAM: 186 GB
- Kernel: 4.18.0-553.89.1.el8_10.x86_64
- Linux: Rocky Linux 8.10 (Green Obsidian)
Wersja pigz: 2.4
Testy przeprowadzono w urządzeniu /dev/shm, aby wyeliminować maksymalnie wpływ prędkości pamięci masowej na wyniki.
Schemat testu był następujący:
cd /dev/shm/test;
cp $start_dir/PragueCorpus.tar .
isize=$(stat --printf="%s" "PragueCorpus.tar")
/usr/bin/time -f "$e" -f time_file pigz -p $nproc PragueCorpus.tar
osize=$(stat --printf="%s" "PragueCorpus.tar.gz")
ctime=$(cat time_file)
/usr/bin/time -f "$e" -f time_file pigz -p $nproc -d PragueCorpus.tar.gz
dtime=$(cat time_file)
echo pigz $ctime $dtime $isize $osize $nproc
Z uzyskanych danych obliczamy stopień kompresji dzieląc wartości isize przez
osize, oraz przyspieszenie dzieląc czas kompresji dla danej liczby wątków
przez czas dla jednego wątku.
Wyniki wyglądają następująco:
| Liczba wątków | Czas kompresji | Czas dekompresji | Stopień kompresji | Przyspieszenie |
|---|---|---|---|---|
| 1.00 | 2.68 | 0.30 | 1.60 | 1.00 |
| 2.00 | 1.37 | 0.25 | 1.60 | 1.96 |
| 3.00 | 0.91 | 0.25 | 1.60 | 2.95 |
| 4.00 | 0.69 | 0.25 | 1.60 | 3.88 |
| 5.00 | 0.55 | 0.25 | 1.60 | 4.87 |
| 6.00 | 0.47 | 0.25 | 1.60 | 5.70 |
| 7.00 | 0.40 | 0.25 | 1.60 | 6.70 |
| 8.00 | 0.35 | 0.25 | 1.60 | 7.66 |
| 9.00 | 0.32 | 0.25 | 1.60 | 8.38 |
| 10.00 | 0.28 | 0.25 | 1.60 | 9.57 |
| 11.00 | 0.26 | 0.25 | 1.60 | 10.31 |
| 12.00 | 0.24 | 0.25 | 1.60 | 11.17 |
| 13.00 | 0.23 | 0.25 | 1.60 | 11.65 |
| 14.00 | 0.21 | 0.25 | 1.60 | 12.76 |
| 15.00 | 0.20 | 0.25 | 1.60 | 13.40 |
| 16.00 | 0.19 | 0.25 | 1.60 | 14.11 |
| 17.00 | 0.18 | 0.25 | 1.60 | 14.89 |
| 18.00 | 0.17 | 0.25 | 1.60 | 15.76 |
| 19.00 | 0.16 | 0.25 | 1.60 | 16.75 |
| 20.00 | 0.16 | 0.25 | 1.60 | 16.75 |
| 21.00 | 0.15 | 0.25 | 1.60 | 17.87 |
| 22.00 | 0.15 | 0.25 | 1.60 | 17.87 |
| 23.00 | 0.14 | 0.25 | 1.60 | 19.14 |
| 24.00 | 0.14 | 0.25 | 1.60 | 19.14 |
| 25.00 | 0.13 | 0.25 | 1.60 | 20.62 |
| 26.00 | 0.13 | 0.25 | 1.60 | 20.62 |
| 27.00 | 0.13 | 0.25 | 1.60 | 20.62 |
| 28.00 | 0.13 | 0.25 | 1.60 | 20.62 |
| 29.00 | 0.13 | 0.25 | 1.60 | 20.62 |
| 30.00 | 0.12 | 0.25 | 1.60 | 22.33 |
| 31.00 | 0.12 | 0.25 | 1.60 | 22.33 |
| 32.00 | 0.12 | 0.25 | 1.60 | 22.33 |
| 33.00 | 0.12 | 0.25 | 1.60 | 22.33 |
| 34.00 | 0.12 | 0.25 | 1.60 | 22.33 |
| 35.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 36.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 37.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 38.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 39.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 40.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 41.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 42.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 43.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 44.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 45.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 46.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 47.00 | 0.11 | 0.25 | 1.60 | 24.36 |
| 48.00 | 0.11 | 0.25 | 1.60 | 24.36 |
Jak widać, problem czasu kompresji się zmniejsza - do pewnego momentu, czas dekompresji i stopień pozstaje stały, przyspieszenie jest widczone.
Przyjżyjmy się wynikom dokładniej za pomocą wykresu

Widać, że przyspieszenie kompresji jest wyraźne w zakresie od 2 do około 26 rdzeni. Powyżej tej liczby przyspieszenie nie rośnie już znacząco, uzyskuje maksimum przy około 35 rdzeniach. Do około 12 rdzeni przyspieszenie jest niemal liniowo proporcjonlane do liczby użytych wątków (zielona linia), powyżej tej wartości zaczyna co raz bardziej odstawać, aby przy około 35 rdzeniach przestać całkowicie rosnąć.
Przyczyny takiego zachowania mogą być różne: rodzielanie zadań przez program, zapisywanie danych do pliku, sposób pracy maszyny z dwoma dwurdzeniowymi procesorami, dynamiczne zmiany w taktowaniu procesora w zależności od obciążenia. W tym artykule nie wyjaśnimy tego do końca. Są to jednak zjawiska znane z działania programów równoległych.
Poodkreślmy jednak, że uzyskaliśmy ponad 20-krotne skrócenie czasu kompresji danych.
Warto jeszcze wspomnieć o czasie wypakowania. Jak widać w tabeli, nie ma tu tak zanczącego przyspieszenia, a właściwie jest tylko pewien przeskok międzu jednym a wieloma wątkami. Dzieje się tak ponieważ, pigz musi wypakować i zapisać dane w odpowiedniej kolejności i nie można tego zrównoleglić. Jeśli przy dekompresji jest dostępne więcej niż jeden rdzeń, pigz, tworzy jeden wątek dekompresji i trzy pomocnicze wątki do odczytu, zapisu i werfikacji danych.
Podsumowanie
Poznaliśmy funkcję programu do kompresji danych polegająca na wykorzystaniu procesora wielordzeniowego do przyspieszenia kompresji danych.
Nauczyliśmy się, że warto czasem poszukać alternatywnego rozwiązania (pigz) dla znanego narzędzia (gzip), bo może ono oferwać lepsze funkcjie niż orginał.
Warto analizować działanie programów na konkretnych danych, zawłaszcza tych które wymagają pewnych nieco większych zasobów komputera, czy też czasu pracy procesora.
Program pigz.
Strona domowa: https://zlib.net/pigz/
Licencja: Licenzja zlib (wolne oprogramowanie)