Brotli - poziomy kompresji

W systemach Linux jest dostępnych wiele narzędzi do kompresji danych. Obok tak popularnych jak zip, gzip czy bzip2, spotkamy szereg mniej znanych komend. Jedną z nich jest brotli.

Brotli to algorytm i zestaw narzędzi stworzony przez Google do poprawy wydajności przesyłu danych za pomocą protokołu HTTP. Później został przystosowany do zastosowań ogólnych i jako polecenie brotli jest dostępny w wielu dystrybucjach Linuksa.

System został stworzony jako lepszy zamiennik gzip i algorytmu Defalte również stosowanego kompresji strumieni danych w HTTP. Brotli podobnie jak Deflate bazuje na algorytmie LZ77 i kodowaniu Huffmana. Brotli jednak zawiera sporo różnic, m.in. predefiniowany słownik 13 tys popularnych słów i łańcuchów znaków opracowany na podstawie dużego zbioru danych tekstowych i HTML. Brotli stosuje także dynamiczne okno robocze: w Deflate są do 32 kB, w Brotli rozmiar okna może się zmieniać od 1 kB do 16MB. Pozwala to, w teorii, lepiej pakować większe pliki.

W naszym przeglądzie narzędzi do kompresji brotli wypadło zaskakująco słabo jeśli chodzi o czas kompresji, dlatego postanowiliśmy przyjrzeć się bliżej działania tego narzędzia. Przypomnijmy wyniki w zestawieniu z gzip:

Program Czas kompresji Czas dekompresji Stopień kompresji
brotli 145.98 0.31 1.96
gzip 2.09 0.32 1.60

Nie wygląda to dobrze. Dla pewności powtórzyliśmy testy na tej samej maszynie oraz na dwóch innych. Dla punktu odniesienia zrobiliśmy też pomiary dla gzip.

Maszyny, system operacyjny i wersje brotli użyte w tym teście:

"core-i5" "xeon-platinum" "xeon-gold"
CPU 6-Core Intel Core i5-10500 2 x 24-Core Intel Xeon Platinum 8268 1 x 24-Core INTEL XEON GOLD 6542Y
RAM 32 GB 186 GB 2 GB
Kernel 6.1.0-28-amd64 4.18.0-553.89.1.el8_10.x86_64 6.12.48+deb13-amd64
Linux MX-23.6 Rocky Linux 8.10 (Green Obsidian) Debian GNU/Linux 13 (trixie)
brotli 1.0.9 1.0.6 1.1.0

Do testów wykorzystano zbiór danych z projektu Prague Corpus

Testy przeprowadzono w urządzeniu /dev/shm, aby wyeliminować maksymalnie wpływ prędkości pamięci masowej na wyniki. Użyto polecenia brotli (oraz gzip) bez włączania żadnych opcji. 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 brotli PragueCorpus.tar

osize=$(stat --printf="%s" "PragueCorpus.tar.br")
ctime=$(cat time_file)
/usr/bin/time -f "$e" -f time_file brotli -d PragueCorpus.tar.br
dtime=$(cat time_file)
echo brotli $ctime $dtime $isize $osize

Następnie osobno obliczono stopień kompresji dzieląc wartości isize przez osize

Wyniki kształtują się następująco:

Program Maszyna Czas kompresji Czas dekompresji Stopień kompresji
brotli core-i5 145.98 0.31 1.955108
brotli xeon-gold 123.68 0.32 1.955108
brotli xeon-platinum 204.64 0.39 1.955108
gzip core-i5 2.09 0.32 1.601789
gzip xeon-gold 2.11 0.34 1.601789
gzip xeon-platinum 2.73 0.42 1.601789

Jak widać, problem czasu kompresji najwyraźniej nie zależy od maszyny ani od środowiska. Kompresja za pomocą domyślnych opcji brotli trwa zatrważająco długo. Pocieszające jest, że dekompresja jest szybka i właściwie nie zależna od maszyny i wersji programu. Również stopień kompresji nie zależy do wersji, przynajmniej w takim zakresie jak to przetestowano.

W czym zatem tkwi problem? Aby to wyjaśnić przyjrzyjmy się jakie opcje i przełączniki mają programy do kompresji. W przypadku brotli wyglada to tak:

$ brotli  --help
Usage: brotli [OPTION]... [FILE]...
Options:
  -#                          compression level (0-9)
  -c, --stdout                write on standard output
  -d, --decompress            decompress
  -f, --force                 force output file overwrite
  -h, --help                  display this help and exit
  -j, --rm                    remove source file(s)
  -k, --keep                  keep source file(s) (default)
  -n, --no-copy-stat          do not copy source file(s) attributes
  -o FILE, --output=FILE      output file (only if 1 input file)
  -q NUM, --quality=NUM       compression level (0-11)
  -t, --test                  test compressed file integrity
  -v, --verbose               verbose mode
  -w NUM, --lgwin=NUM         set LZ77 window size (0, 10-24)
                              window size = 2**NUM - 16
                              0 lets compressor choose the optimal value
  --large_window=NUM          use incompatible large-window brotli
                              bitstream with window size (0, 10-30)
                              WARNING: this format is not compatible
                              with brotli RFC 7932 and may not be
                              decodable with regular brotli decoders
  -S SUF, --suffix=SUF        output file suffix (default:'.br')
  -V, --version               display version and exit
  -Z, --best                  use best compression level (11) (default)
Simple options could be coalesced, i.e. '-9kf' is equivalent to '-9 -k -f'.
With no FILE, or when FILE is -, read standard input.
All arguments after '--' are treated as files.

Programy do kompresji takie jak brotli pozwalają regulować stopień kompresji za pomocą przełączników "-#", gdzie "#" oznacza liczbę z zakresu 0-9, przy czym "0" oznacza niski stopień kompresji, a 9 - wysoki. Podobną funkcję ma też gzip. Uzyskanie większego stopienia kompresji okupione jest dłuższym czasem pracy programu. W brotli stopień kompresji można też przełączać za pomocą opcji "-q NUM", z odpowiednimi liczbami. Tu okazuje się, że są dostępne jeszcze dwa poziomy: 10 i 11. Na koniec widzimy, że najwyższy stopień kompresji można włączyć także za pomocą "-Z", oraz, że to jest domyślny tryb działania aplikacji!

Sprawdźmy jak to wygląda w praktyce

Program Poziom kompresji Czas kompresji Czas dekompresji Stopień kompresji
brotli 1 0.19 0.23 1.531879
brotli 2 0.34 0.22 1.584773
brotli 3 0.42 0.21 1.587900
brotli 4 0.75 0.20 1.602303
brotli 5 1.59 0.21 1.669438
brotli 6 2.16 0.21 1.675939
brotli 7 3.99 0.21 1.685410
brotli 8 7.59 0.21 1.688666
brotli 9 26.24 0.21 1.699232
brotli 10 81.34 0.32 1.946666
brotli 11 142.42 0.31 1.955108

Zobaczmy zależność czasu kompresji od zadanego poziomu kompresji dla brotli na wykresie:

Czas kompresji

Widać, że dla wysokich poziomów 10-11 czas kompresji rośnie kilkuset krotnie. A więc mamy przyczynę problemu z brotli: program domyślnie stosuje najwyższy poziom kompresji co jedna wymaga dużo czasu na pakowanie. Dla niskich poziomów 1-6, ten czas jest całkiem krótki. Zwróćmy uwagę, że jest porównywalny (2.16 s) z gzip z poprzednich testów (2.09 s).

Jednak czy długi czas kompresji rzeczywiście opłaca się pod względem uzyskanego stopnia kompresji?

Stopień kompresji

W przypadków poziomów 10-11 kompresja jest znacząco wyższa niże przy niższych poziomach. Jednak jest to wzrost w stosunku 1.95 do 1.67 przy czasie kompresji rosnącym w stosunku 142.42 do 2.16.

Jeśli zależało nam będzie na szybkości działania warto użyć opcji przyspieszających pracę np.

$ brotli -6 plik

Jeśli chcemy uzyskać duży stopień kompresji chyba warto rozejrzeć się za innymi narzędziami, które oferują podobny stopień upakowania ale w krótszym czasie czasie. Oczywiście wiele zależy od rodzaju danych.

Podsumowanie

Poznaliśmy funkcję programu kompresji polegająca na regulacji stopnia kompresji, które wpływa zarówno na wielkość skompresowanych plików jak i na czas kompresji.

Nauczyliśmy się, że warto dokładnie prześledzić opcje programu, bo domyślne działanie może nie być tym czego byśmy oczekiwali, ale program ma jednak przydatne funkcje. Podobnie bywa z wieloma poleceniami w Linuksie.

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.

Pozostaje tylko pytanie: dlaczego autorzy brotli postawili na taki wariant domyślnego działania, gdzie czas kompresji jest bardzo niekorzystny, co może zniechęcać do użycia tego narzędzia? Na pewno priorytetem był stopień kompresji, ale czy warto, aż tak długo czekać?

Strona domowa: https://github.com/google/brotli

Licencja: MIT

benchmark desktop linux polecenia serwer terminal