Самый быстрый возможный grep

Я хотел бы знать, есть ли какой-либо совет, чтобы сделать grep как можно быстрее. У меня есть довольно большая база текстовых файлов для поиска в максимально возможной степени. Я сделал все в нижнем регистре, чтобы я мог избавиться от опции -i. Это значительно ускоряет поиск.

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

Есть ли у кого-нибудь опыт ускорения grep? Может быть, скомпилировать его с нуля с определенным флагом (я нахожусь в Linux CentOS), упорядочить файлы определенным образом или, возможно, сделать параллельный поиск каким-то образом?

Ответ 1

Попробуйте GNU parallel, который включает пример того, как для использования с grep:

grep -r greps рекурсивно через каталоги. На многоядерных процессорах 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-agent, чтобы избежать паролей):

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

Ответ 2

Если вы ищете очень большие файлы, то может помочь установка вашего языка.

GNU grep работает намного быстрее в языке C, чем с UTF-8.

export LC_ALL=C

Ответ 3

Ripgrep утверждает, что теперь он самый быстрый.

https://github.com/BurntSushi/ripgrep

Также включает parallelism по умолчанию

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

Из README

Он построен поверх механизма регулярного выражения Rust. Rage regex engine использует конечные автоматы, SIMD и агрессивные литералы, чтобы сделать поиск очень быстро.

Ответ 5

Не строго улучшение кода, но то, что я нашел полезным после запуска grep на 2+ миллионах файлов.

Я перенес операцию на дешевый SSD-накопитель (120 ГБ). Примерно в $100 это доступный вариант, если вы регулярно хрустаете много файлов.

Ответ 6

Если вам неважно, какие файлы содержат эту строку, вы можете выделить чтение и grepping на два задания, так как это может быть дорогостоящим для появления grep много раз - один раз для каждого небольшого файла.

  • Если у вас один очень большой файл:

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

  • Многие небольшие сжатые файлы (отсортированные по индексу)

    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 {}

Ответ 7

Основываясь на ответе Сандро, я посмотрел ссылку, которую он предоставил здесь и играл с BSD grep vs. GNU grep. Мои быстрые результаты теста показали: GNU grep - путь, путь быстрее.

Итак, моя рекомендация по оригинальному вопросу "Самый быстрый возможный grep": убедитесь, что вы используете GNU grep, а не BSD grep (который по умолчанию используется для MacOS).

Ответ 8

Я лично использую ag (серебряный искатель) вместо grep, и это быстрее, также вы можете комбинировать его с параллельным и трубным блоками.

https://github.com/ggreer/the_silver_searcher

Обновление: Теперь я использую https://github.com/BurntSushi/ripgrep, который быстрее, чем ag, в зависимости от вашего варианта использования.

Ответ 9

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

Имея файл идентификаторов, который вы хотите найти в большом файле с именем my_ids.txt Имя файла 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 no point in allocating more greps than x files

В моем случае это сократило бы то, что было бы 17-часовой работой в 1 час 20-минутной работы. Я уверен, что здесь есть какая-то колоколообразная кривая эффективности, и очевидно, что доступ к имеющимся ядрам не принесет вам никакой пользы, но это было гораздо лучшее решение, чем любое из приведенных выше комментариев для моих требований, как указано выше. Это имеет дополнительное преимущество по сравнению с script параллелью при использовании в основном (linux) собственных инструментов.

Ответ 10

cgrep, если он доступен, может быть на порядок быстрее, чем grep.

Ответ 11

MCE 1.508 включает в себя оболочку <файл, список > } с двумя кусками script, поддерживающую множество бинарных файлов 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 не вызывает под-оболочку (sh -c/path/to/grep) при вызове двоичного файла.

Другой альтернативой является модуль MCE:: Grep, включенный в MCE.

Ответ 12

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

После его компиляции (требуется golang), вы можете индексировать папку с помощью

# index current folder
cindex .

Индекс будет создан в ~/.csearchindex

Теперь вы можете выполнить поиск:

# search folders previously indexed with cindex
csearch eggs

Я все еще собираю результаты через grep, чтобы получить раскрашенные совпадения.