A solução geral que abrange todos os tamanhos dos seus arquivos: link
EXEMPLO: Grepping n linhas para m expressões regulares.
A solução mais simples para o grep de um arquivo grande para muitos regexps é:
grep -f regexps.txt bigfile
Ou se os regexps forem sequências fixas:
grep -F -f regexps.txt bigfile
Existem 3 fatores limitantes: CPU, RAM e E / S de disco.
A RAM é fácil de medir: se o processo grep ocupa a maior parte da sua memória livre (por exemplo, quando a parte superior é executada), a RAM é um fator limitante.
A CPU também é fácil de medir: se o grep levar 90% da CPU no topo, a CPU é um fator limitante e a paralelização acelerará isso.
É mais difícil ver se a E / S de disco é o fator limitante e, dependendo do sistema de disco, pode ser mais rápido ou mais lento paralelizar. A única maneira de saber com certeza é testar e medir.
Fator limitante: RAM
O grep normal -f regexs.txt bigfile funciona não importa o tamanho do bigfile, mas se o regexps.txt é tão grande que não cabe na memória, então você precisa dividir isso.
grep -F leva cerca de 100 bytes de RAM e o grep leva cerca de 500 bytes de RAM por 1 byte de regexp. Então, se regexps.txt é 1% da sua memória RAM, então pode ser muito grande.
Se você puder converter seus regexps em strings fixas, faça isso. Por exemplo. Se as linhas que você está procurando no bigfile, tudo se parece com:
ID1 foo bar baz Identifier1 quux
fubar ID2 foo bar baz Identifier2
então seu regexps.txt pode ser convertido de:
ID1.*Identifier1
ID2.*Identifier2
para:
ID1 foo bar baz Identifier1
ID2 foo bar baz Identifier2
Desta forma, você pode usar o grep -F, que consome cerca de 80% menos memória e é muito mais rápido.
Se ainda não couber na memória, você pode fazer isso:
parallel --pipepart -a regexps.txt --block 1M grep -F -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
O 1M deve ser sua memória livre dividida pelo número de núcleos e dividida por 200 para grep -F e por 1000 para grep normal. No GNU / Linux você pode fazer:
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+://'
Se você pode viver com linhas duplicadas e ordem errada, é mais rápido fazer isso:
parallel --pipepart -a regexps.txt --block $percpu --compress grep -F -f - bigfile
Fator limitante: CPU
Se a CPU é o fator limitante, a paralelização deve ser feita no regexps:
cat regexp.txt | parallel --pipe -L1000 --round-robin --compress grep -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
O comando iniciará um grep por CPU e lerá bigfile uma vez por CPU, mas como isso é feito em paralelo, todas as leituras, exceto a primeira, serão armazenadas em cache na RAM. Dependendo do tamanho de regexp.txt, pode ser mais rápido usar --block 10m ao invés de -L1000.
Alguns sistemas de armazenamento têm melhor desempenho ao ler vários blocos em paralelo. Isso vale para alguns sistemas RAID e para alguns sistemas de arquivos de rede. Para paralelizar a leitura de bigfile:
parallel --pipepart --block 100M -a bigfile -k --compress grep -f regexp.txt
Isso dividirá o bigfile em blocos de 100MB e executará o grep em cada um desses blocos. Para paralelizar a leitura de bigfile e regexp.txt, combine os dois usando --fifo:
parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \
\| parallel --pipe -L1000 --round-robin grep -f - {}
Se uma linha corresponder a várias expressões regulares, a linha poderá ser duplicada.
Problema maior
Se o problema for grande demais para ser resolvido, provavelmente você está pronto para o Lucene.