Próbuję wykonać rejestr rekordów dla pliku gzip o wielkości 7,6 GB. Znalazłem kilka podejść przy użyciu zcat
polecenia.
$ zcat T.csv.gz | wc -l
423668947
To działa, ale zajmuje zbyt dużo czasu (więcej niż 10 minut, aby uzyskać licznik). Próbowałem jeszcze kilku takich podejść
$ sed -n '$=' T.csv.gz
28173811
$ perl -lne 'END { print $. }' < T.csv.gz
28173811
$ awk 'END {print NR}' T.csv.gz
28173811
Wszystkie trzy te polecenia działają dość szybko, ale dają niepoprawną liczbę 28173811.
Jak mogę wykonać rekord w minimalnym czasie?
Odpowiedzi:
Te
sed
,perl
iawk
polecenia, które można wymienić poprawne, ale wszystko przeczytać skompresowane dane i liczby znaków nowego wiersza w tym. Te znaki nowej linii nie mają nic wspólnego ze znakami nowej linii w nieskompresowanych danych.Aby policzyć liczbę wierszy w nieskompresowanych danych, nie ma sposobu na ich rozpakowanie. Twoje podejście
zcat
jest prawidłowe, a ponieważ dane są tak duże, ich rozpakowanie zajmie trochę czasu.Większość narzędzi zajmujących się
gzip
kompresją i dekompresją najprawdopodobniej wykorzysta do tego te same procedury bibliotek współdzielonych. Jedynym sposobem na jego przyspieszenie byłoby znalezienie implementacjizlib
procedur, które są w jakiś sposób szybsze od domyślnych, i przebudowanie np. Ichzcat
użycie.źródło
zcat
. Znaczna część pracyzcat
polega na generowaniu rzeczywistej produkcji. Ale jeśli liczysz tylko\n
postacie, nie jest to konieczne.gzip
kompresja działa zasadniczo poprzez zastąpienie zwykłych długich łańcuchów krótszymi łańcuchami. Musisz więc dbać tylko o długie ciągi w słowniku, które zawierają\n
, i policz (ważone) ich wystąpienie. Np. Z powodu angielskich zasad.\n
jest to wspólny ciąg 16 bitów.Użyj unpigz.
Odpowiedź Kusalananda jest poprawna, to będzie trzeba rozpakować, że cały plik do skanowania jego zawartość.
/bin/gunzip
robi to tak szybko, jak to możliwe, na jednym rdzeniu. Pigz to równoległa implementacja,gzip
która może wykorzystywać wiele rdzeni.Niestety, dekompresja sama od zwykłych plików gzip nie można parallelized, ale
pigz
nie oferują ulepszoną wersjęgunzip
,unpigz
, że robi prac związanych, takich jak czytanie, pisanie, i sum kontrolnych w osobnym wątku. W niektórych szybkich testach porównawczychunpigz
jest prawie dwa razy szybszy niżgunzip
na moim podstawowym komputerze i5.Zainstaluj
pigz
z ulubionym menedżerem pakietów i używajunpigz
zamiastgunzip
lubunpigz -c
zamiastzcat
. Twoje polecenie staje się:Wszystko to zakłada, że wąskim gardłem jest procesor, a nie dysk.
źródło
pigz
strona podręcznika stwierdza, że dekompresji nie można zrównoleglać, przynajmniej nie bez specjalnie przygotowanych do tego celu strumieni deflacji. W rezultacie pigz używa pojedynczego wątku (głównego wątku) do dekompresji, ale utworzy trzy inne wątki do czytania, pisania i sprawdzania obliczeń, co w niektórych okolicznościach może przyspieszyć dekompresję . Mimo to, podobnie jak ty, uważam, że jest co najmniej dwa razy szybszy niżgzip
, jeśli nie z powodu równoległościProblem ze wszystkimi rurociągami polega na tym, że zasadniczo podwajasz pracę. Bez względu na to, jak szybka jest dekompresja, dane nadal muszą zostać przeniesione do innego procesu.
Perl ma PerlIO :: gzip, który pozwala bezpośrednio czytać strumienie gzip. Dlatego może oferować przewagę, nawet jeśli jego prędkość dekompresyjna może nie odpowiadać
unpigz
:Próbowałem go z 13 MB skompresowanym plikiem gzip (dekompresuje się do 1,4 GB) na starym MacBooku Pro 2010 z 16 GB pamięci RAM i starym ThinkPad T400 z 8 GB pamięci RAM z plikiem już w pamięci podręcznej. Na Macu skrypt Perla był znacznie szybszy niż przy użyciu potoków (5 sekund vs 22 sekund), ale na ArchLinux stracił rozpakowanie:
przeciw
i
Oczywiste jest, że używanie
unpigz -c file.gz | wc -l
jest tutaj zwycięzcą zarówno pod względem szybkości. I ta prosta linia poleceń z pewnością przewyższa pisanie programu, nawet najkrótszego.źródło
gzip | wc
ma taką samą prędkość jak skrypt w perlu. Ipigz | wc
jest dwukrotnie szybszy.gzip
działa z tą samą prędkością, niezależnie od tego, czy wypiszę dane wyjściowe do / dev / null lub potok do.wc
Uważam, że „biblioteka gzip” używana przez perla jest szybsza niż narzędzie wiersza poleceń gzip. Być może istnieje inny specyficzny problem Mac / Darwin z rurami. To wciąż niesamowite, że ta wersja perla jest w ogóle konkurencyjna.zcat
i gorzej niżunpigz
. Dziwi mnie, jak szybszy jest potok w systemie Linux w porównaniu z komputerem Mac. Nie spodziewałem się tego, chociaż powinienem, jak kiedyś zauważyłem, ten sam program działał szybciej na Linux VM z ograniczoną mocą procesora na tym samym komputerze Mac niż na gołym metalu.zcat | wc -l
, a twój skrypt perla 5,5 sekundy. Szczerze mówiąc, jestem zdumiony różnorodnością, którą zgłaszają ludzie, szczególnie między Linuksem a MacOS X!wc -l
zajmuje 2,5 sekundy.gzcat compressed.gz > /dev/null
zajmuje 2,7 sekundy. Rurociąg trwa jednak 22 sekundy. Jeśli spróbuję GNUwc
, zajmuje to tylko pół sekundy na zdekompresowanym pliku, ale 22 sekundy w potoku. Wykonanie GNUzcat
zajmuje dwa razy więcej czasuzcat compressed.gz > /dev/null
. To jest na Mavericks, stary procesor Core 2 Duo, 16 GB pamięci RAM, Crucial MX100 SSD.Odpowiedź Kusalanandy jest w większości poprawna. Aby policzyć linie, musisz wyszukać nowe linie. Jednak teoretycznie możliwe jest wyszukiwanie nowych linii bez całkowitego rozpakowywania pliku.
gzip używa kompresji DEFLATE. DEFLATE to kombinacja kodowania LZ77 i Huffmana. Może istnieć sposób, aby odkryć tylko węzeł symbolu Huffmana dla nowej linii i zignorować resztę. Prawie na pewno istnieje sposób na wyszukiwanie nowych linii zakodowanych za pomocą L277, utrzymywanie liczby bajtów i ignorowanie wszystkiego innego.
Więc IMHO teoretycznie możliwe jest znalezienie rozwiązania bardziej wydajnego niż unpigz czy zgrep. To powiedziawszy, z pewnością nie jest praktyczne (chyba że ktoś już to zrobił).
źródło
Można to zrobić za
zgrep
pomocą-c
flagi i$
parametru.W takim przypadku -c poinstruuj komendę, aby wypisała liczbę dopasowanych linii, a regex $ dopasowuje koniec linii, aby pasował do każdej linii lub pliku.
Jak komentował @ StéphaneChazelas -
zgrep
to tylko skrypt wokółzcat
igrep
powinny zapewniać podobną wydajność do oryginalnego sugestięzcat | wc -l
źródło
zgrep
jest ogólnie skryptem, który wywołujezcat
(tak samo jakgzip -dcq
), aby rozpakować dane i nakarmić jegrep
, więc nie pomoże.Jak widać, większość odpowiedzi próbuje zoptymalizować to, co potrafi: liczbę przełączników kontekstu i międzyoperacyjne operacje wejścia / wyjścia. Powodem jest to, że jest to jedyne, co można tutaj łatwo zoptymalizować.
Problem polega na tym, że zapotrzebowanie na zasoby jest prawie nieistotne w stosunku do zapotrzebowania na zasoby podczas dekompresji. Właśnie dlatego optymalizacje nie przyspieszają niczego.
Tam, gdzie można to naprawdę przyspieszyć, byłby to zmodyfikowany algorytm rozpakowywania (tj. Dekompresji), który pomija faktyczną produkcję rozpakowanego strumienia danych; raczej oblicza tylko liczbę nowych linii w zdekompresowanym strumieniu ze skompresowanego . Byłoby ciężko, wymagałaby głębokiej znajomości algorytmu gzip (pewnej kombinacji algorytmów kompresji LZW i Huffmana ). Jest całkiem prawdopodobne, że algorytm nie pozwala znacząco zoptymalizować czasu dekompresji przy oświetleniu, że musimy tylko znać liczbę nowych linii. Nawet jeśli byłoby to możliwe, zasadniczo powinna zostać opracowana nowa biblioteka dekompresyjna gzip (nie istnieje, dopóki się nie dowie).
Realistyczna odpowiedź na twoje pytanie brzmi: nie, nie możesz uczynić go znacznie szybszym.
Być może przydałaby Ci się równoległa dekompresja gzip, jeśli istnieje. Może używać wielu rdzeni procesora do dekompresji. Jeśli nie istnieje, można go stosunkowo łatwo opracować.
Dla xz istnieje równoległy kompresor (pxz).
źródło