Wieloprocesorowe programy do pakowania plików - serwery Ux

Obecnie niedrogie serwery z wieloma procesorami, czy rdzeniami, są czymś powszechnym. Jednak standardowe programy do kompresji i dekompresji są nadal jednowątkowe, chociaż dostępne są też programy pakujące potrafiące wykorzystać wieloprocesorowość. Dlatego przeprowadziliśmy testy w tym zakresie. Analizując uzyskane dane można wybrać odpowiedni kompresor, w zależności od typu przetwarzanych danych, wymaganego stopnia kompresji, czy też szybkości działania. W ten sposób mamy dane pozwalające wybrać, do prowadzonych projektów najbardziej, adekwatny program.
Do testów wybrałem serwer Intel XEON E5-2699V3 @ 2,3GHz (turbo: 3,6 GHz), 22 nm 18 rdzeni, 36 wątków, 45 MB SmartCache wraz ze 120 GB RAM. Ponieważ pracuję na na maszynie wirtualnej dostępne do testów będzie 16 rdzeni, a dzięki technologii Hyper-Threading system operacyjny będzie widział 32 rdzenie. Serwer w tej konfiguracji wybrałem z powodu tego, że potrzeba zastąpienia standardowych kompresorów zazwyczaj wystąpi przy przetwarzaniu dużych ilości danych, a taki stan łącze ze średnimi i dużymi systemami informatycznymi.
Do testów wybrałem najbardziej popularne programu, a przez to najbardziej sprawdzone. Oprócz programów obecnych w minimalnych dystrybucjach, jak: tar, gzip, bzip2 testom poddaliśmy doinstalowane programy: pigz, lbzip2, pbzip2, squashfs. Należy dokładnie zapoznać się z instrukcjami od poszczególnych programów decyzją o ich użyciu. Czasami np. dekompresja wykorzystuje tylko ograniczoną liczbę rdzeni.

Do testów przygotowaliśmy cztery zestawy danych:
1) Wygenerowane pliki logów. Cztery pliki o rozmiarze 1 GB (po 20 milionów inni w każdym z plików), oraz dwa pliki 100 MB (po 2 miliony inni w każdym z plików). Łącznie 2,3 GB danych zawierających 84 miliony linii. Dane w likach zawierają wiele wspólnych elementów, jak to zazwyczaj w logach, i powinny podlegać znacznej kompresji.
2) 2,3 GB skompresowanych danych w 16 plikach. Do tego testu przygotowaliśmy celowo 16 identycznych plików, by pokazać ciekawą funkcjonalność jednego z użytych programów. Ten test polega na tym, by wykazać zachowanie się programów kompresujących, gdy dane są już mocno spakowane - dlatego filmy są idealne do takiego testu.
3) Kolejny test obejmuje strukturę 107 650 katalogów zawierającymi 380 607 plików. Trzykrotnie rozpakowałem kolekcję FreeBSD Ports (ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/ports.tar.gz). Tym sposobem można sprawdzić zachowanie się programów przy dużych ilościach plików i katalogów, gdyż nie zawsze obsługa systemu plików jest ich najmocniejszą stroną.
4) Test ostatni polegać będzie na kompresowaniu jednego dużego pliku. Symuluję tutaj kompresowanie dump'a z bazy danych. Zazwyczaj dump'y z bazy danych kompresują się podobnie, jak pliki tekstowe, w stosunku od 1:10 do 1:20. Kompresowanie pojedynczego pliku pozwala sprawdzić zachowanie sie kompresorów wieloprocesorowych w takiej sytuacji. Użyty plik jest prawdziwym dump'em bazy, a jego rozmiar jest kompromisem, pomiędzy zrzutem z dużej bazy, a czasem wykonania testów.

Przygotowane dane do testów:

Poniżej zamieszczam screenshot'y z testów.
Dają istotne informacje o wykorzystaniu zasobów przez różne programy w czasie pracy - zarówno dla wielu plików, jak i dla pojedynczego pliku.

Wykorzystanie procesorów przez jednowątkowy program kompresujący bzip2:

Kompresowanie programem lbzip2:

Kompresowanie programem pbzip2:

Spakowane trzy typy danych, oprócz testu pliku dump.sql, z podziałem na programy kompresujące:

Kompresowanie programem squashfs wielu małych plików:

Kompresowanie programem squashfs dużego pojedynczego pliku:

System dysków nie był ograniczeniem dla tych testów:

Rozpakowanie programem pigz:

Rozpakowanie programem lbzip2:

Rozpakowanie programem squashfs wielu małych plików:

Rozpakowanie programem squashfs pojedynczego pliku:

Dane z testów:


*** Compress ***



Katalog: logRozmiar (kB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do time mksquashfs log/ "$i"log.sq ; done117 31215,41314,97815,314
for i in {1..3}; do time tar --use-compress-program=lbzip2 -cf "$i"log.lbzip2 log/; done180 9274,8264,8424,911
for i in {1..3}; do time tar -cjf "$i"log.tar.gz2 log/; done181 439423,130421,484423,268
for i in {1..3}; do time tar --use-compress-program=pbzip2 -cf "$i"log.pbzip2 log/; done181 66216,60015,98716,376
for i in {1..3}; do tar -czf $1log.tar.gz log/; done282 86662,91162,52161,621
for i in {1..3}; do time tar --use-compress-program=pigz -cf "$i"log.tar.pigz log/; done283 1752,5752,7382,856
for i in {1..3}; do time tar -cf "$i"log.tar log/; done2 289 0001,9261,9251,911
Katalog: movieRozmiar (kB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do time tar -cf "$i"movie.tar movie/; done2 337 0001,9491,9671,949
for i in {1..3}; do time tar --use-compress-program=pigz -cf "$i"movie.tar.pigz movie/; done2 335 0003,6983,6823,557
for i in {1..3}; do time tar --use-compress-program=lbzip2 -cf "$i"movie.lbzip2 movie/; done2 344 00011,17711,22011,180
for i in {1..3}; do time mksquashfs movie/ "$i"movie.sq ; done14914,79514,62214,516
for i in {1..3}; do time tar --use-compress-program=pbzip2 -cf "$i"movie.pbzip2 movie/; done2 344 00015,92416,12516,900
for i in {1..3}; do time tar -czf "$i"movie.tar.gz movie/; done2 334 00090,48091,46790,887
for i in {1..3}; do time tar -cjf "$i"movie.tar.gz2 movie/; done2 344 000394,723394,035394,080
Katalog: portsRozmiar (kB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do time tar --use-compress-program=pigz -cf "$i"ports.tar.pigz ports/; done156 7493,4943,4223,499
for i in {1..3}; do time tar --use-compress-program=lbzip2 -cf "$i"ports.lbzip2 ports/; done123 9923,6683,7343,437
for i in {1..3}; do time tar -cf "$i"ports.tar ports/; done1 077 0003,8223,4473,471
for i in {1..3}; do time tar --use-compress-program=pbzip2 -cf "$i"ports.pbzip2 ports/; done124 0744,0664,4794,180
for i in {1..3}; do time mksquashfs ports/ "$i"ports.sq ; done52 47612,76812,14112,398
for i in {1..3}; do time tar -czf "$i"ports.tar.gz ports/; done157 12126,85925,23425,244
for i in {1..3}; do time tar -cjf "$i"ports.tar.gz2 ports/; done121 77192,58192,07092,169

Przed niektórymi poleceniami dałem komendy kasujące poprzednie archiwa, gdyż niektóre programy były na to "czułe".
Warto zwrócić uwagę na rozmiar archiwum katalogu "movie", gdy kompresorem był squashfs - przypomnę, że do tego testu przygotowaliśmy 16 kopii tego samego pliku. Jak widać program squashfs skorzystał z tego skwapliwie, podobnie jak potrafią to robić nowoczesne systemy plików (jak np.: ZFS).


*** Decompres ***
Katalog: logRozmiar (kB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do rm *.txt ; time unsquashfs -f -d . "$i"log.sq ; done---1,9031,7471,924
for i in {1..3}; do time tar -xf "$i"log.tar ; done---2,0222,3752,542
for i in {1..3}; do time tar --use-compress-program=pbzip2 -xf "$i"log.pbzip2 ; done---3,7624,1684,751
for i in {1..3}; do time tar --use-compress-program=lbzip2 -xf "$i"log.lbzip2 ; done---3,8443,7384,302
for i in {1..3}; do time tar --use-compress-program=pigz -xf "$i"log.tar.pigz ; done---4,7035,8965,416
for i in {1..3}; do time tar -xzf "$i"log.tar.gz ; done---14,96514,48514,353
for i in {1..3}; do time tar -xjf "$i"log.tar.gz2 ; done---71,02771,13571,540
Katalog: movieRozmiar (kB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do rm *.mp4 ; time unsquashfs -f -d . "$i"movie.sq ; done---1,7391,6931,691
for i in {1..3}; do time tar -xf "$i"movie.tar ; done---2,0612,4473,211
for i in {1..3}; do time tar --use-compress-program=lbzip2 -xf "$i"movie.lbzip2 ; done---6,3036,1226,137
for i in {1..3}; do time tar --use-compress-program=pigz -xf "$i"movie.tar.pigz ; done---6,7117,1046,487
for i in {1..3}; do time tar --use-compress-program=pbzip2 -xf "$i"movie.pbzip2 ; done---7,0847,1577,251
for i in {1..3}; do time tar -xzf "$i"movie.tar.gz ; done---17,35017,44717,518
for i in {1..3}; do time tar -xjf "$i"movie.tar.gz2 ; done---197,856197,948199,170
Katalog: portsRozmiar (kB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do rm -r ports ; time tar --use-compress-program=pbzip2 -xf "$i"ports.pbzip2 ; done---21,85024,22925,791
for i in {1..3}; do rm -r ports-0? ; sync ;time unsquashfs -f -d . "$i"ports.sq ; done---22,52623,98022,605
for i in {1..3}; do rm -r ports ; time tar -xf "$i"ports.tar ; done---25,38225,48526,900
for i in {1..3}; do rm -r ports ; time tar --use-compress-program=lbzip2 -xf "$i"ports.lbzip2 ; done---25,94024,94426,902
for i in {1..3}; do rm -r ports ; time tar --use-compress-program=pigz -xf "$i"ports.tar.pigz ; done---27,69529,62724,790
for i in {1..3}; do rm -r ports ; time tar -xzf "$i"ports.tar.gz ; done---33,67832,63030,443
for i in {1..3}; do rm -r ports ; time tar -xjf "$i"ports.tar.gz2 ; done---52,31553,88754,021


*** SQL compress ***Rozmiar (MB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do time tar -cf "$i"sql.tar sql/; done10 1408,8649,04510,354
for i in {1..3}; do time tar --use-compress-program=pigz -cf "$i"sql.tar.pigz sql/; done1 52913,22713,38213,109
for i in {1..3}; do time tar --use-compress-program=lbzip2 -cf "$i"sql.lbzip2 sql/; done1 14622,38721,83922,362
for i in {1..3}; do time mksquashfs sql/ "$i"sql.sq ; done1 46733,97934,23233,757
for i in {1..3}; do time tar --use-compress-program=pbzip2 -cf "$i"sql.pbzip2 sql/; done1 15062,99363,20864,049
for i in {1..3}; do time tar -czf "$i"sql.tar.gz sql/; done1 532298,638294,853293,020
for i in {1..3}; do time tar -cjf "$i"sql.tar.gz2 sql/; done1 1491665,2051662,2651663,702
*** SQL decompres ***Rozmiar (MB)Czas (s)Czas (s)Czas (s)
for i in {1..3}; do rm -r sqldump.sql ; sync ; time unsquashfs -f -d . "$i"sql.sq ; done---7,517,5477,549
for i in {1..3}; do time tar -xf "$i"sql.tar ; done---9,31210,81710,924
for i in {1..3}; do rm -r sql/ ; time tar --use-compress-program=pbzip2 -xf "$i"sql.pbzip2 ; done---12,22812,33812,383
for i in {1..3}; do rm -r sql/ ; time tar --use-compress-program=lbzip2 -xf "$i"sql.lbzip2 ; done---12,92112,96912,871
for i in {1..3}; do rm -r sql/ ; time tar --use-compress-program=pigz -xf "$i"sql.tar.pigz ; done---27,58027,96228,936
for i in {1..3}; do time tar -xzf "$i"sql.tar.gz ; done---74,24776,32279,398
for i in {1..3}; do time tar -xjf "$i"sql.tar.gz2 ; done---273,099272,989272,595

Programy kompresujące:
Program gzip wykorzystuje algorytm kompresji znany jako "DEFLATE" - stosowany np.: w PNG, HTTP, SSH. Jego największa zaletą jest szybkość i niskie zużycie zasobów (choć wymaga czasami więcej pamięci).
Kompresor bzip2 powstał w 1996 r. Jego szczególną cecha jest wysoki stopień kompresji i powolne działania. Zaimplementowano w nie algorytmu Burrows-Wheeler, który jest dużo bardziej skomplikowany niż "DEFLATE" z gzip'a, ale nie wymaga więcej pamięci.
SquashFS w tym teście znalazł się, ponieważ ma bardzo dobre "osiągi". Nie jest to typowy program kompresujący i nie wszędzie znajdzie zastosowanie. Jest to skompresowany system plików (zamontowany jako system plików jest tylko do odczytu). Do kompresji używa programu gzip, ale potrafi też użyć: lzma, lzo, xz. Wykorzystany jest dystrybucje live CD, np.: Backtrack, Debian, Knoppix, OpenWrt. Przykłady użycia:
mksquashfs /some/dir dir.sqsh
mount dir.sqsh /mnt/dir -t squashfs -o loop
/etc/fstab:
/var/arch.sqsh  /var/arch   squashfs    ro,defaults 0 0


Przełączniki dla tar'a:
gzip:
tar czvf misc.tar.gz misc/
bzip2:
tar cjvf misc.tar.bz2 misc/
xz:
tar cJvf misc.tar.xz misc/


********

Więcej informacji:
Informatyka, FreeBSD, Debian


***

Inne wpisy:



Update: 2018.07.17
Create: 2018.07.17