Общее решение, которое охватывает все размеры ваших файлов: https://www.gnu.org/software/parallel/man.html#EXAMPLE:-Grepping- n-строк-для-m-регулярных-выражений
ПРИМЕР: выделение n строк для m регулярных выражений.
Самым простым решением для большого количества регулярных выражений с помощью grep является:
grep -f regexps.txt bigfile
Или, если регулярные выражения представляют собой фиксированные строки:
grep -F -f regexps.txt bigfile
Есть 3 ограничивающих фактора: ЦП, ОЗУ и дисковый ввод-вывод.
ОЗУ легко измерить: если процесс grep занимает большую часть вашей свободной памяти (например, при запуске в верхней части), то ОЗУ является ограничивающим фактором.
CPU также легко измерить: если grep занимает> 90% CPU в верхней части, то CPU является ограничивающим фактором, и распараллеливание ускорит это.
Труднее увидеть, является ли дисковый ввод-вывод ограничивающим фактором, и в зависимости от дисковой системы распараллеливание может быть быстрее или медленнее. Единственный способ узнать наверняка - это проверить и измерить.
Ограничивающий фактор: RAM
Обычный bigfile grep -f regexs.txt работает независимо от размера bigfile, но если regexps.txt настолько велик, что не помещается в памяти, то вам необходимо разделить его.
grep -F занимает около 100 байт ОЗУ, а grep занимает около 500 байт ОЗУ на 1 байт регулярного выражения. Поэтому, если regexps.txt занимает 1% вашей оперативной памяти, он может быть слишком большим.
Если вы можете преобразовать регулярные выражения в фиксированные строки, сделайте это. Например. если все строки, которые вы ищете в большом файле, выглядят так:
ID1 foo bar baz Identifier1 quux
fubar ID2 foo bar baz Identifier2
, тогда ваш regexps.txt может быть преобразован из:
ID1.*Identifier1
ID2.*Identifier2
в:
ID1 foo bar baz Identifier1
ID2 foo bar baz Identifier2
Таким образом вы можете использовать grep -F, который занимает примерно на 80% меньше памяти и намного быстрее.
Если он по-прежнему не помещается в памяти, вы можете сделать следующее:
parallel --pipepart -a regexps.txt --block 1M grep -F -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
1M должен быть вашей свободной памятью, разделенной на количество ядер и разделенной на 200 для grep -F и на 1000 для обычного grep. В GNU / Linux вы можете:
free=$(awk '/^((Swap)?Cached|MemFree|Buffers):/ { sum += $2 }
END { print sum }' /proc/meminfo)
percpu=$((free / 200 / $(parallel --number-of-cores)))k
parallel --pipepart -a regexps.txt --block $percpu --compress grep -F -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
Если вы можете жить с дублированными строками и неправильным порядком, это быстрее сделать:
parallel --pipepart -a regexps.txt --block $percpu --compress grep -F -f - bigfile
Ограничивающий фактор: CPU
Если CPU является ограничивающим фактором, распараллеливание должно выполняться на regexps:
cat regexp.txt | parallel --pipe -L1000 --round-robin --compress grep -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
Команда запускает одну команду grep для каждого процессора и читает большой файл один раз для каждого процессора, но поскольку это выполняется параллельно, все операции чтения, кроме первого, будут кэшироваться в ОЗУ. В зависимости от размера regexp.txt может быть быстрее использовать --block 10m вместо -L1000.
Некоторые системы хранения работают лучше при параллельном чтении нескольких фрагментов.Это верно для некоторых систем RAID и некоторых сетевых файловых систем. Чтобы распараллелить чтение большого файла:
parallel --pipepart --block 100M -a bigfile -k --compress grep -f regexp.txt
Это разделит большой файл на блоки по 100 МБ и запустит команду grep для каждого из этих блоков. Чтобы распараллелить чтение bigfile и regexp.txt, объедините их с помощью --fifo:
parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \
\| parallel --pipe -L1000 --round-robin grep -f - {}
Если строка соответствует нескольким регулярным выражениям, строка может быть продублирована.
Большая проблема
Если проблема слишком велика, чтобы ее решить, вы, вероятно, готовы к Lucene.