Zastanawiałem się, czy można zrobić coś bardziej wydajnego niż dekompresję od początku pliku do samego momentu. Wygląda na to, że odpowiedź brzmi „nie”. Jednak na niektórych procesorach (Skylake) zcat | tail
nie przyspiesza procesora do pełnej prędkości zegara. Patrz poniżej. Niestandardowy dekoder może uniknąć tego problemu i zapisać wywołania systemowe zapisu potoku, a może być o ~ 10% szybszy. (Lub ~ 60% szybciej w Skylake, jeśli nie zmienisz ustawień zarządzania energią).
Najlepsze, co możesz zrobić z dostosowanym zlibem z skipbytes
funkcją, to parsowanie symboli w bloku kompresji, aby dojść do końca bez konieczności rekonstrukcji zdekompresowanego bloku. Może to być znacznie szybsze (prawdopodobnie co najmniej 2x) niż wywołanie zwykłej funkcji dekodowania zlib w celu zastąpienia tego samego bufora i przejścia do przodu w pliku. Ale nie wiem, czy ktoś napisał taką funkcję. (I myślę, że to tak naprawdę nie działa, chyba że plik został napisany specjalnie, aby umożliwić dekoderowi ponowne uruchomienie w określonym bloku).
Miałem nadzieję, że istnieje sposób na przeskakiwanie bloków Deflate bez ich dekodowania, ponieważ byłoby to znacznie szybsze. Drzewo Huffmana jest wysyłane na początku każdego bloku, więc możesz dekodować od początku dowolnego bloku (tak myślę). Och, myślę, że stan dekodera jest czymś więcej niż drzewem Huffmana, jest to również poprzednie 32kB zdekodowanych danych i nie jest to domyślnie resetowane / zapominane ponad granicami bloków. Do tych samych bajtów można się ciągle odwoływać, więc mogą pojawić się dosłownie tylko raz w gigantycznym skompresowanym pliku. (np. w pliku dziennika nazwa hosta prawdopodobnie pozostaje „gorąca” w słowniku kompresji przez cały czas i każde jego wystąpienie odnosi się do poprzedniego, a nie pierwszego).
zlib
Instrukcja mówi, trzeba użyć Z_FULL_FLUSH
podczas rozmowy deflate
, jeśli chcesz, aby strumień sprężonego być możliwy do przeszukania do tego punktu. „Resetuje stan kompresji”, więc myślę, że bez tego referencje wstecz mogą przejść do poprzednich bloków. Tak więc, chyba że plik zip został napisany z okazjonalnymi pełnymi blokami (jak każdy 1G lub coś miałoby nieistotny wpływ na kompresję), myślę, że będziesz musiał wykonać więcej pracy dekodowania do pożądanego poziomu niż ja początkowo myślący. Myślę, że prawdopodobnie nie możesz zacząć od początku żadnego bloku.
Resztę tego napisałem, gdy myślałem, że można po prostu znaleźć początek bloku zawierającego pierwszy bajt, który chcesz, i stamtąd zdekodować.
Ale niestety początek bloku Deflate nie wskazuje, jak długo jest on w przypadku bloków skompresowanych. Dane nieskompresowane mogą być kodowane za pomocą nieskompresowanego typu bloku, który ma 16-bitowy rozmiar w bajtach z przodu, ale bloki skompresowane nie: RFC 1951 opisuje format dość czytelnie . Bloki z dynamicznym kodowaniem Huffmana mają drzewo z przodu bloku (więc dekompresor nie musi szukać w strumieniu), więc kompresor musi zachować cały (skompresowany) blok w pamięci przed jego zapisaniem.
Maksymalna odległość odniesienia do tyłu wynosi tylko 32 kB, więc kompresor nie musi przechowywać w pamięci dużej ilości nieskompresowanych danych, ale to nie ogranicza rozmiaru bloku. Bloki mogą mieć wiele megabajtów. (Jest to wystarczająco duży rozmiar, aby poszukiwania dysku były tego warte nawet na napędzie magnetycznym, w przeciwieństwie do sekwencyjnego odczytu do pamięci i po prostu pomijania danych w pamięci RAM, jeśli można było znaleźć koniec bieżącego bloku bez parsowania go).
zlib tworzy bloki tak długo, jak to możliwe:
Według Marc Adler , zlib rozpoczyna nowy blok dopiero po zapełnieniu bufora symboli, który przy ustawieniu domyślnym to 16 383 symboli (literałów lub dopasowań)
Skopiowałem dane wyjściowe seq
(który jest wyjątkowo redundantny i dlatego prawdopodobnie nie jest to świetny test), ale pv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
na tym działa tylko przy ~ 62 MiB / s skompresowanych danych na Skylake i7-6700k przy 3,9 GHz, z DDR4-2666 RAM. To 246 Mb / s zdekompresowanych danych, co jest zmianą w porównaniu do memcpy
prędkości ~ 12 GiB / s dla bloków o zbyt dużych rozmiarach, aby zmieścić się w pamięci podręcznej.
(Przy energy_performance_preference
ustawieniu domyślnym balance_power
zamiast balance_performance
, wewnętrzny regulator procesora Skylake decyduje się na działanie tylko przy 2,7 GHz, ~ 43 MiB / s skompresowanych danych. Używam go, sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
aby go ulepszyć. Prawdopodobnie tak częste wywołania systemowe nie wyglądają jak prawdziwe związane z procesorem pracuj do jednostki zarządzania energią.)
TL: DR: zcat | tail -c
jest związany z procesorem nawet na szybkim procesorze, chyba że masz bardzo wolne dyski. gzip wykorzystał 100% procesora, na którym działał (i uruchomił instrukcje 1,81 na zegar, zgodnie z perf
) i tail
wykorzystał 0,162 procesora, na którym działał (0,58 IPC). System był poza tym w większości bezczynny.
Używam Linuksa 4.14.11-1-ARCH, który ma domyślnie włączoną KPTI do pracy w Meltdown, więc wszystkie te write
wywołania systemowe gzip
są droższe niż kiedyś: /
Posiadanie wbudowanego wyszukiwania do ( unzip
lub zcat
nadal używanie zwykłej zlib
funkcji dekodowania) uratuje wszystkie zapisy potokowe i sprawi, że procesory Skylake będą działały z pełną prędkością zegara. (Ta redukcja w dół dla niektórych rodzajów obciążenia jest unikalna dla Intel Skylake i późniejszych, które odciążają proces podejmowania decyzji o częstotliwości procesora z systemu operacyjnego, ponieważ mają więcej danych na temat tego, co robi procesor, i mogą szybciej rosnąć / zwalniać. To jest normalnie dobre, ale tutaj prowadzi do tego, że Skylake nie przyspiesza do pełnej prędkości przy bardziej konserwatywnym ustawieniu gubernatora).
Żadne wywołania systemowe, po prostu przepisanie bufora pasującego do pamięci podręcznej L2, dopóki nie osiągniesz żądanej początkowej pozycji bajtu, prawdopodobnie spowodowałoby co najmniej kilka% różnic. Może nawet 10%, ale tutaj tylko tworzę liczby. Nie profilowałem zlib
szczegółowo, aby zobaczyć, jak duży jest rozmiar pamięci podręcznej i jak bardzo opróżnianie TLB (a zatem opróżnianie pamięci podręcznej uop) przy każdym wywołaniu systemowym boli przy włączonym KPTI.
Istnieje kilka projektów oprogramowania, które dodają indeks wyszukiwania do formatu pliku gzip . Nie pomaga to, jeśli nie możesz wygenerować widocznych skompresowanych plików, ale inni przyszli czytelnicy mogą skorzystać.
Przypuszczalnie żaden z tych projektów nie ma funkcji dekodowania, która wie, jak przeskakiwać strumień Deflate bez indeksu, ponieważ są one zaprojektowane do działania tylko wtedy, gdy indeks jest dostępny.