Найшвидший grep


80

Я хотів би знати, чи є якісь підказки, щоб зробити grepце якомога швидше. Я маю досить велику базу текстових файлів для найшвидшого пошуку. Я зробив їх усі малими літерами, щоб я міг позбутися -iваріанту. Це робить пошук набагато швидшим.

Крім того, я з’ясував, що -Fі -Pрежими швидші, ніж стандартний. Я використовую перший, коли рядок пошуку не є регулярним виразом (просто звичайний текст), другий, якщо йдеться про регулярний вираз.

Хтось має досвід прискорення grep? Можливо, скомпілювати його з нуля за допомогою певного прапора (я на Linux CentOS), впорядкувати файли певним чином або, можливо, зробити паралельним пошук якимось чином?


1
Це завжди однаковий набір файлів? Якщо ви виявили, що шукаєте той самий (великий) набір файлів grep, можливо, саме час шукати рішення для правильного їх індексування ("найкраще" рішення буде залежати від того, які це файли).
FatalError

так, це той самий набір файлів. ти думаєш, що таке повнотекстове рішення, як люцен, покращило б продуктивність? як правило, потрібно близько 30/40 секунд, щоб здійснити пошук 2500 файлів (кожен - літературна книга) на загальну кількість слів близько 250 млн. слів.
фісташка

1
"...or maybe make the search parallel in some way?"Я був би дуже радий почути про це. grepповинен повністю мати можливість працювати паралельно, але я підозрюю, що пошук все ще може бути прив'язаний до операцій вводу-виводу.
Конрад,

2
Ви пробували використовувати ack-grep?
meder omuraliev

2
Використовуйте ack-grepабо краще Ag! geoff.greer.fm/2011/12/27/the-silver-searcher-better-than-ack
Ніколас Вілсон

Відповіді:


104

Спробуйте GNU паралельно , що включає приклад того, як використовувати його зgrep :

grep -rgreps рекурсивно через каталоги. На багатоядерних процесорах GNU parallelчасто може це прискорити.

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

Це запустить 1,5 завдання на ядро ​​і надасть 1000 аргументів grep.

Для великих файлів він може розділити вхід на декілька фрагментів із аргументами --pipeта --block:

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

Ви також можете запустити його на декількох різних машинах через SSH (ssh-агент, необхідний для уникнення паролів):

parallel --pipe --sshlogin server.example.com,server2.example.net grep foo < bigfile

5
використовувати --color=alwaysдля збереження кольору grep (це вірно, коли ви також використовуєте grep у трубі)
Джим

2
Якщо findмає -print0предикат (більшість із них), то переважно було б використовувати find . -type f -print0 | parallel -0 -k …. Мій приклад man(1) parallelнасправді говорить це. Крім того, я підозрюю, що globstarви можете зробити це ще швидше, якщо ви shopt -s globstar; parallel -k -j150% -n 1000 -m fgrep -H -n STRING ::: **/*.c
дотримуєтесь

3
@WilliamPursell це корисне використання, catякщо ви хочете sudoотримати доступbigfile
Jayen

2
Чому ви встановлюєте 1,5 завдання на ядро? Чому б не 1 робота на ядро?
JohnGalt

2
@JohnGalt Часто дискові вводи-виводи зупиняють один із процесів. Запустивши кілька більше, ніж ядер, все одно можна буде зробити всі ядра - навіть якщо деякі завдання чекають даних. Налаштуйте 150%, щоб побачити, що найкраще працює у вашій системі.
Ole Tange

70

Якщо ви шукаєте у дуже великих файлах, тоді налаштування мовної мови дійсно може допомогти.

GNU grep працює набагато швидше в мові C, ніж у UTF-8.

export LC_ALL=C

1
Вражаюче, схоже, ця одинарна лінія забезпечує вдвічі більшу швидкість.
Федір РИХТІК

Хтось може пояснити, чому це?
Robert E Mealey

5
"Просте порівняння байтів проти порівняння багатобайтних символів" <говорить мій бос ... справа праворуч
Robert E Mealey

7
Отже, це не зовсім безпечно, особливо якщо ви відповідаєте шаблону (на відміну від просто зіставлення рядків) або якщо вміст вашого файлу не ascii. все ж варто робити це в деяких випадках, але будьте обережні.
Robert E Mealey,

@RobertEMealey Чи він сказав "Одинокий" замість "Простий"?
Елайджа Лінн,

12

Ripgrep стверджує, що зараз він найшвидший.

https://github.com/BurntSushi/ripgrep

Також включає паралельність за замовчуванням

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

З README

Він побудований поверх двигуна регулярного виразу Rust. Двигун регулярних виразів Rust використовує обмежені автомати, SIMD та агресивні буквальні оптимізації, щоб зробити пошук дуже швидким.


Це неймовірно швидко!
Бити


4

Не суто вдосконалення коду, але те, що я знайшов корисним після запуску grep на 2+ мільйони файлів.

Я перемістив операцію на дешевий накопичувач SSD (120 Гб). Приблизно 100 доларів - це доступний варіант, якщо ви регулярно обробляєте багато файлів.


3

Якщо вам байдуже, які файли містять рядок, можливо, ви захочете розділити читання та grepping на два завдання, оскільки це може коштувати grepбагато разів, ікру багато разів - один раз для кожного невеликого файлу.

  1. Якщо у вас один дуже великий файл:

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

  2. Багато невеликих стиснених файлів (відсортованих за inode)

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

Зазвичай я стискаю свої файли за допомогою lz4 для максимальної пропускної здатності.

  1. Якщо вам потрібна лише назва файлу, що відповідає:

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


2

Спираючись на відповідь Сандро, я подивився посилання, яке він тут наводив, і погрався з BSD grep проти GNU grep. Мої результати швидкого тестування показали: GNU grep - це набагато швидше.

Тож моя рекомендація до вихідного запитання "якнайшвидший grep": переконайтесь, що ви використовуєте GNU grep, а не BSD grep (що є типовим для MacOS, наприклад).


Я показую BSD Grep швидше на моєму 13-дюймовому MacBook Pro, ніж 8 ГБ, 6-ядерний Linode під час пошуку файлу дампа .sql 250 МБ. 6 с проти 25 с
AnthumChris

2

Я особисто використовую ag (шукач срібла) замість grep, і це набагато швидше, також ви можете поєднувати його з паралельним та конвеєрним блоками.

https://github.com/ggreer/the_silver_searcher

Оновлення: Зараз я використовую https://github.com/BurntSushi/ripgrep, який швидший, ніж ag, залежно від вашого випадку використання.


Я знайшов помилку в цьому. Іноді це не заглиблюється в дерево, і у мене є випадки, коли grep показує результат, а ag - ні. Я не можу піти на компроміси щодо точності щодо швидкості.
username_4567

1
Вам слід відкрити випуск на їх обліковому записі github і повідомити про це (я б це зробив, але не можу повторити його), оскільки до цього часу я не виявив жодних неточностей. Напевно вони розберуться з цим, і так, ви маєте рацію, я повністю згоден: точність насамперед.
Jinxmcg

1

Одне, що я швидше знайшов для використання grep для пошуку (особливо для зміни шаблонів) в одному великому файлі, це використання split + grep + xargs з паралельним прапором. Наприклад:

Маючи файл ідентифікаторів, який ви хочете шукати у великому файлі під назвою my_ids.txt Ім'я bigfile bigfile.txt

Використовуйте split, щоб розділити файл на частини:

# 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

У моєму випадку це скоротило б те, що було б 17-годинною роботою, на 1-годинну 20-хвилинну роботу. Я впевнений, що тут є якась крива дзвоника щодо ефективності, і, очевидно, перегляд наявних ядер не дасть вам жодної користі, але це було набагато кращим рішенням, ніж будь-який із наведених вище коментарів щодо моїх вимог, як зазначено вище. Це має додаткову перевагу порівняно зі сценарієм паралельного використання в основному власних інструментів (Linux).


0

cgrep, якщо він доступний, може бути на порядок швидше, ніж grep.


0

MCE 1.508 включає подвійний сценарій обгортки {file, list}, що підтримує багато бінарних файлів C. agrep, grep, egrep, fgrep та tre-agrep.

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

https://metacpan.org/release/MCE

Не потрібно конвертувати в малу літеру, коли хочеться, щоб -i швидко працював. Просто передайте --lang = C в mce_grep.

Порядок виводу зберігається. Вивід -n та -b також правильний. На жаль, це не так для паралелі GNU, згаданої на цій сторінці. Я дуже сподівався, що GNU Parallel працюватиме тут. Крім того, mce_grep робить НЕ суб-оболонки (ш -c / шлях / до / Grep) при виклику виконуваного файлу.

Інший варіант - модуль MCE :: Grep, що входить до складу MCE.


Вам потрібно надати застереження, будучи автором зазначеного інструменту.
FractalSpace

0

Невелике відхилення від початкової теми: індексовані утиліти командного рядка пошуку з проекту googlecodesearch набагато швидші, ніж grep: https://github.com/google/codesearch :

Після того, як ви скомпілюєте його ( потрібен пакет golang ), ви можете проіндексувати папку за допомогою:

# index current folder
cindex .

Індекс буде створений під ~/.csearchindex

Тепер ви можете шукати:

# search folders previously indexed with cindex
csearch eggs

Я все ще передаю результати через grep, щоб отримати кольорові збіги.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.