Grepping um arquivo enorme (80 GB) de alguma forma para acelerá-lo?

113
 grep -i -A 5 -B 5 'db_pd.Clients'  eightygigsfile.sql

Ele está rodando há uma hora em um servidor Linux bastante poderoso, que de outra forma não está sobrecarregado. Alguma alternativa ao grep? Algo sobre minha sintaxe que possa ser melhorado (egrep, fgrep melhor?)

O arquivo está realmente em um diretório que é compartilhado com uma montagem em outro servidor, mas o espaço em disco real é local, então isso não deve fazer nenhuma diferença?

o grep está ocupando até 93% da CPU

zzapper
fonte
8
Dependendo da sua localidade, a -iopção pode desacelerar o processo, tente sem -iou com LC_ALL=C grep .... Além disso, se você estiver apenas procurando por uma string fixa, use grep -F.
Thor
5
Como @dogbane mencionou, usar a variável LC_ALL = C junto com fgrep pode acelerar sua pesquisa. Fiz alguns testes e consegui alcançar um aumento de desempenho de 1400% e escrevi um artigo detalhado porque isso está em meu post de
aceleração do
Estou curioso - qual arquivo tem 80 GB de tamanho? Eu gostaria de pensar que, quando um arquivo fica tão grande, pode haver uma estratégia de armazenamento melhor (por exemplo, girar arquivos de log ou categorizar hierarquicamente em diferentes arquivos e pastas). Além disso, se as mudanças ocorrerem apenas em certos lugares do arquivo (por exemplo, no final), então apenas armazene alguns resultados do grep da seção anterior que não mudam e, em vez de fazer o grep do arquivo original, faça o grep do arquivo de resultado armazenado.
Sridhar Sarnobat
Eu escolhi github.com/google/codesearch - tanto a indexação quanto a pesquisa são muito rápidas (escrito em Go). cindex .para indexar sua pasta atual, então csearch db_pd.Clients.
ccpizza
1
Se o seu arquivo fosse indexado ou classificado, isso poderia ser muito mais rápido. Pesquisar cada linha é O (n) por definição, enquanto um arquivo classificado pode ser pesquisado dividindo-o ao meio - ponto em que você falaria menos de um segundo para pesquisar seus 80 GB (daí porque um banco de dados indexado de 80 GB leva pouco tempo para um SELECT simples, enquanto seu grep leva ... bem, o tempo que leva).
Charles Duffy

Respostas:

148

Aqui estão algumas opções:

1) Prefixe seu comando grep com LC_ALL=Cpara usar a localidade C em vez de UTF-8.

2) Use fgrepporque você está procurando uma string fixa, não uma expressão regular.

3) Remova a -iopção, se você não precisar dela.

Portanto, seu comando se torna:

LC_ALL=C fgrep -A 5 -B 5 'db_pd.Clients' eightygigsfile.sql

Também será mais rápido se você copiar o arquivo para o disco RAM.

dogbane
fonte
5
isso foi MUITO mais rápido por uma ordem de magnitude, obrigado. BTW, adicionei -n para obter os números das linhas. Também pode ser um -m para sair após a partida
zzapper
5
Uau, muito obrigado @dogbane ótima dica! Isso me levou a um túnel de pesquisa para descobrir por que LC_ALL = C acelera o grep e foi uma experiência muito esclarecedora!
JacobN
7
Algumas pessoas (não eu) gostam grep -Fmais defgrep
Walter Tross
2
Meu entendimento é que LANG=C(em vez de LC_ALL=C) é o suficiente e é mais fácil de digitar.
Walter Tross de
2
@Adrian fgrepé outra forma de escrever grep -F, como man fgreplhe direi. Algumas versões do mantambém dizem que o primeiro está obsoleto para o último, mas a forma mais curta é muito conveniente para morrer.
Walter Tross
36

Se você tem uma CPU multicore, eu realmente recomendo o GNU parallel . Para usar grep em um arquivo grande em paralelo:

< eightygigsfile.sql parallel --pipe grep -i -C 5 'db_pd.Clients'

Dependendo de seus discos e CPUs, pode ser mais rápido ler blocos maiores:

< eightygigsfile.sql parallel --pipe --block 10M grep -i -C 5 'db_pd.Clients'

Sua pergunta não ficou totalmente clara, mas outras opções grepincluem:

  • Largando o -i bandeira.
  • Usando o -F bandeira para uma string fixa
  • Desativando NLS com LANG=C
  • Definir um número máximo de correspondências com a -mbandeira.
Steve
fonte
2
Se for um arquivo real, use em --pipepartvez de --pipe. É muito mais rápido.
Ole Tange
Este padrão de uso não suporta o espaço, precisamos usar assim: parallel --pipe --block 10M "/ usr / bin / grep -F -C5 -e 'Animal Care & Pets'"
zw963
O que significa o <caractere que precede o comando paralelo?
elcortegano
1
@elcortegano: Isso é o que é chamado de I / O redirecionamento . Basicamente, ele lê a entrada do seguinte nome de arquivo. Semelhante a, cat file.sql | parallel ...mas evita um UUOC . Paralelo GNU também tem uma maneira de ler a entrada de um arquivo usando parallel ... :::: file.sql. HTH.
Steve
10

Algumas melhorias triviais:

  • Remova a opção -i, se possível, a distinção entre maiúsculas e minúsculas é bastante lenta.

  • Substitua o . por\.

    Um único ponto é o símbolo regex para corresponder a qualquer caractere, que também é lento

BeniBela
fonte
3

Duas linhas de ataque:

  • tem certeza, você precisa do -i ou você tem a possibilidade de se livrar dele?
  • Você tem mais núcleos para brincar? grepé de thread único, então você pode querer começar mais deles em deslocamentos diferentes.
Eugen Rieck
fonte
1
< eightygigsfile.sql parallel -k -j120% -n10 -m grep -F -i -C 5 'db_pd.Clients'  

Se você precisar pesquisar várias strings, grep -f strings.txt economiza muito tempo. O texto acima é uma tradução de algo que estou testando no momento. os valores das opções -j e -n parecem funcionar melhor para o meu caso de uso. O grep -F também fez uma grande diferença.

user584583
fonte