Najszybszy możliwy grep

80

Chciałbym wiedzieć, czy jest jakaś wskazówka, którą należy zrobić grepjak najszybciej. Mam dość dużą bazę plików tekstowych do przeszukania w możliwie najszybszy sposób. Zrobiłem je wszystkie małymi literami, aby pozbyć się -iopcji. To znacznie przyspiesza wyszukiwanie.

Dowiedziałem się też, że tryby -Fi -Ptryby są szybsze niż domyślne. Pierwszego używam, gdy szukany ciąg nie jest wyrażeniem regularnym (tylko zwykłym tekstem), drugiego, jeśli chodzi o wyrażenie regularne.

Czy ktoś ma jakieś doświadczenie w przyspieszaniu grep? Może skompiluj go od podstaw z jakąś konkretną flagą (jestem na Linux CentOS), uporządkuj pliki w określony sposób, a może w jakiś sposób spraw, by wyszukiwanie było równoległe?

pistacchio
źródło
1
Czy to zawsze ten sam zestaw plików? Jeśli okaże się, że przeszukujesz ten sam (duży) zestaw plików grep, być może nadszedł czas, aby poszukać rozwiązania, które umożliwi ich prawidłowe indeksowanie („najlepsze” rozwiązanie będzie zależeć od tego, jakiego rodzaju są to pliki).
FatalError
tak, to ten sam zestaw plików. myślisz, że pełnotekstowe rozwiązanie, takie jak lucene, poprawiłoby wydajność? generalnie przeszukanie 2500 plików (każdy z nich to książka literacka) zajmuje około 30/40 sekund, aby uzyskać łączną liczbę słów wynoszącą około 250 milionów słów.
pistacchio
1
"...or maybe make the search parallel in some way?"Byłbym naprawdę podekscytowany, gdybym o tym usłyszał. greppowinien całkowicie działać równolegle, ale podejrzewam, że wyszukiwanie może nadal być ograniczone we / wy.
Conrad Dean
2
Czy próbowałeś użyć ack-grep?
meder omuraliev
2
Użyj ack-greplub lepiej Ag! geoff.greer.fm/2011/12/27/the-silver-searcher-better-than-ack
Nicholas Wilson

Odpowiedzi:

104

Wypróbuj równolegle z GNU , który zawiera przykład, jak go używać zgrep :

grep -rgreps rekurencyjnie przez katalogi. W przypadku procesorów wielordzeniowych GNU parallelczęsto może to przyspieszyć.

find . -type f | parallel -k -j150% -n 1000 -m grep -H -n STRING {}

Spowoduje to uruchomienie 1,5 zadania na rdzeń i przekazanie 1000 argumentów grep.

W przypadku dużych plików może podzielić dane wejściowe na kilka części za pomocą argumentów --pipei --block:

 parallel --pipe --block 2M grep foo < bigfile

Możesz również uruchomić go na kilku różnych maszynach przez SSH (ssh-agent potrzebny do uniknięcia haseł):

parallel --pipe --sshlogin server.example.com,server2.example.net grep foo < bigfile
Chewie
źródło
5
użyj --color=alwaysdo zachowania koloru grepa (dotyczy to również sytuacji, gdy używasz grepa w rurze)
Jim,
2
Jeśli findma -print0predykat (większość tak), lepiej byłoby go użyć find . -type f -print0 | parallel -0 -k …. Mój przykład man(1) parallelfaktycznie to mówi. Podejrzewam też, że globstarmożesz to zrobić jeszcze szybciej, jeśli szukasz konkretnego wzoru pliku:shopt -s globstar; parallel -k -j150% -n 1000 -m fgrep -H -n STRING ::: **/*.c
kojiro
3
@WilliamPursell to przydatne zastosowanie, catjeśli chcesz sudouzyskać dostępbigfile
Jayen
2
Dlaczego ustalasz 1,5 zadania na rdzeń? Dlaczego nie 1 zadanie na rdzeń?
JohnGalt
2
@JohnGalt Często we / wy dysku wstrzymuje jeden z procesów. Uruchamiając kilka więcej niż jest rdzeni, nadal będzie wiele do zrobienia dla wszystkich rdzeni - nawet jeśli kilka zadań oczekuje na dane. Dostosuj 150%, aby zobaczyć, co działa najlepiej w Twoim systemie.
Ole Tange
70

Jeśli szukasz bardzo dużych plików, ustawienie ustawień regionalnych może naprawdę pomóc.

GNU grep działa dużo szybciej w locale C niż w UTF-8.

export LC_ALL=C
daveb
źródło
1
Imponujące, wygląda na to, że ta pojedyncza linia zapewnia dwukrotnie większą prędkość.
Fedir RYKHTIK
Czy ktoś może wyjaśnić, dlaczego tak jest?
Robert E Mealey
5
„Proste porównanie bajtów a porównanie znaków wielobajtowych” <mówi mój szef ... racja, racja
Robert E Mealey
7
Więc to nie jest dokładnie bezpieczne, zwłaszcza jeśli dopasowujesz wzorce (w przeciwieństwie do dopasowywania ciągów) lub jeśli zawartość twojego pliku nie jest ascii. nadal warto to robić w niektórych przypadkach, ale zachowaj ostrożność.
Robert E Mealey
@RobertEMealey Czy powiedział „Single” zamiast „Simple”?
Elijah Lynn,
12

Ripgrep twierdzi, że jest teraz najszybszy.

https://github.com/BurntSushi/ripgrep

Domyślnie obejmuje również równoległość

 -j, --threads ARG
              The number of threads to use.  Defaults to the number of logical CPUs (capped at 6).  [default: 0]

Z pliku README

Jest zbudowany na bazie silnika regex Rusta. Silnik regex Rusta wykorzystuje automaty skończone, SIMD i agresywne dosłowne optymalizacje, aby wyszukiwanie było bardzo szybkie.

rado
źródło
To jest niesamowicie szybkie!
Pokonaj
4

Nie tylko ulepszenie kodu, ale coś, co uznałem za pomocne po uruchomieniu grepa na ponad 2 milionach plików.

Operację przeniosłem na tani dysk SSD (120 GB). Za około 100 USD jest to niedroga opcja, jeśli regularnie przetwarzasz wiele plików.

Wędrowiec
źródło
3

Jeśli nie dbasz o to, które pliki zawierają ciąg, możesz chcieć rozdzielić czytanie i grepowanie na dwa zadania, ponieważ grepwielokrotne odradzanie się może być kosztowne - raz dla każdego małego pliku.

  1. Jeśli masz jeden bardzo duży plik:

    parallel -j100% --pipepart --block 100M -a <very large SEEKABLE file> grep <...>

  2. Wiele małych skompresowanych plików (posortowanych według i-węzła)

    ls -i | sort -n | cut -d' ' -f2 | fgrep \.gz | parallel -j80% --group "gzcat {}" | parallel -j50% --pipe --round-robin -u -N1000 grep <..>

Zwykle kompresuję pliki za pomocą lz4 dla maksymalnej przepustowości.

  1. Jeśli chcesz tylko nazwę pliku z dopasowaniem:

    ls -i | sort -n | cut -d' ' -f2 | fgrep \.gz | parallel -j100% --group "gzcat {} | grep -lq <..> && echo {}

Alex V
źródło
2

Opierając się na odpowiedzi Sandro, spojrzałem na odniesienie, które tu podał i bawiłem się z BSD grep kontra GNU grep. Moje szybkie wyniki testów porównawczych pokazały: GNU grep jest o wiele szybszy.

Tak więc moja rekomendacja do pierwotnego pytania „najszybszy możliwy grep”: Upewnij się, że używasz GNU grep zamiast BSD grep (co jest na przykład domyślne w MacOS).

Chris
źródło
Wyświetlam BSD Grep szybciej na moim 13-calowym MacBooku Pro niż 8 GB, 6-rdzeniowy Linode podczas wyszukiwania pliku zrzutu 250 MB .sql. 6 s vs 25 s
AnthumChris
2

Osobiście używam ag (srebrnego poszukiwacza) zamiast grepa i jest znacznie szybszy, można go również połączyć z blokiem równoległym i rurowym.

https://github.com/ggreer/the_silver_searcher

Aktualizacja: teraz używam https://github.com/BurntSushi/ripgrep, który jest szybszy niż ag, w zależności od przypadku użycia.

Jinxmcg
źródło
Znalazłem w tym błąd. Czasami nie wchodzi głęboko w drzewo i zdarzają się przypadki, w których grep pokazuje wynik, ale ag nie. Nie mogę iść na kompromis w kwestii dokładności dla szybkości.
username_4567
1
Powinieneś otworzyć problem na ich koncie github i zgłosić go (zrobiłbym to, ale nie mogę tego powtórzyć), ponieważ do tej pory nie znalazłem żadnych nieścisłości. Na pewno to rozwiążą i tak, masz rację, całkowicie się zgadzam: dokładność przede wszystkim.
Jinxmcg,
1

Jedną rzeczą, którą znalazłem szybciej przy używaniu grep do wyszukiwania (szczególnie do zmiany wzorców) w jednym dużym pliku, jest użycie split + grep + xargs z jego flagą równoległą. Na przykład:

Posiadanie pliku z identyfikatorami, które chcesz wyszukać w dużym pliku o nazwie my_ids.txt Nazwa dużego pliku bigfile.txt

Użyj podziału, aby podzielić plik na części:

# Use split to split the file into x number of files, consider your big file
# size and try to stay under 26 split files to keep the filenames 
# easy from split (xa[a-z]), in my example I have 10 million rows in bigfile
split -l 1000000 bigfile.txt
# Produces output files named xa[a-t]

# Now use split files + xargs to iterate and launch parallel greps with output
for id in $(cat my_ids.txt) ; do ls xa* | xargs -n 1 -P 20 grep $id >> matches.txt ; done
# Here you can tune your parallel greps with -P, in my case I am being greedy
# Also be aware that there's no point in allocating more greps than x files

W moim przypadku to zredukowało pracę trwającą 17 godzin do 1 godziny 20 minut. Jestem pewien, że jest tu jakaś krzywa dzwonkowa dotycząca wydajności i oczywiście przeglądanie dostępnych rdzeni nie przyniesie ci nic dobrego, ale było to znacznie lepsze rozwiązanie niż którykolwiek z powyższych komentarzy dla moich wymagań, jak podano powyżej. Ma to dodatkową zaletę w porównaniu ze skryptem równoległym w używaniu głównie (linuxowych) natywnych narzędzi.

user6504312
źródło
0

cgrep, jeśli jest dostępny, może być o rząd wielkości szybszy niż grep.

xhtml
źródło
0

MCE 1.508 zawiera skrypt opakowujący {plik, lista} na poziomie podwójnych porcji, obsługujący wiele plików binarnych C. agrep, grep, egrep, fgrep i tre-agrep.

https://metacpan.org/source/MARIOROY/MCE-1.509/bin/mce_grep

https://metacpan.org/release/MCE

Nie trzeba konwertować na małe litery, jeśli chcemy, aby -i działało szybko. Po prostu przekaż --lang = C do mce_grep.

Kolejność wyników jest zachowywana. Wyjście -n i -b jest również poprawne. Niestety tak nie jest w przypadku równoległego GNU wspomnianego na tej stronie. Naprawdę liczyłem na to, że GNU Parallel będzie tutaj działać. Ponadto mce_grep nie podpowłoce (SH -c / ścieżka / do / grep) przy wywołaniu binarny.

Inną alternatywą jest moduł MCE :: Grep dołączony do MCE.

Mario Roy
źródło
Musisz podać zrzeczenie się odpowiedzialności, będąc autorem wspomnianego narzędzia.
FractalSpace
0

Niewielkie odchylenie od pierwotnego tematu: narzędzia wiersza poleceń wyszukiwania indeksowanego z projektu googlecodesearch są znacznie szybsze niż grep: https://github.com/google/codesearch :

Po skompilowaniu ( plik golang potrzebny jest pakiet ), możesz zindeksować folder za pomocą:

# index current folder
cindex .

Indeks zostanie utworzony w ramach ~/.csearchindex

Teraz możesz wyszukiwać:

# search folders previously indexed with cindex
csearch eggs

Nadal przepuszczam wyniki przez grep, aby uzyskać kolorowe dopasowania.

ccpizza
źródło