Quando grep
ou sed
são usados com a opção --extended-regexp
e o padrão {1,9999}
faz parte do regexp usado, o desempenho desses comandos se torna baixo. Para ser mais claro, abaixo são aplicados alguns testes. [1] [2]
- O desempenho relativo
grep -E
,egrep
esed -E
é quase igual, portanto, apenas o teste que foram feitas comgrep -E
são fornecidos.
Teste 1
$ time grep -E '[0-9]{1,99}' < /dev/null
real 0m0.002s
Teste 2
$ time grep -E '[0-9]{1,9999}' < /dev/null
> real 0m0.494s
Teste 3
$ time grep -E '[0123456789] {1,9999}' </ dev / null > real 21m43.947s
Teste 4
$ time grep -E '[0123456789]+' < /dev/null
$ time grep -E '[0123456789]*' < /dev/null
$ time grep -E '[0123456789]{1,}' < /dev/null
$ time grep -P '[0123456789]{1,9999}' < /dev/null
real 0m0.002s
Qual o motivo dessa diferença significativa de desempenho?
command-line
grep
regex
pa4080
fonte
fonte
[0-9]+
também)time grep -E '[0-9]{1,99}' </dev/null
vs.time grep -E '[0-9]{1,9999}' </dev/null
. Mesmo sem entrada , o segundo comando é lento (em 16.04). Como esperado, omitir-E
e escapar{
e}
se comporta da mesma maneira e a substituição-E
por-P
não é lenta (PCRE é um mecanismo diferente). O mais interessante é o quanto mais rápido[0-9]
é que.
,x
e mesmo[0123456789]
. Com qualquer um desses{1,9999}
,grep
consome uma quantidade enorme de RAM; Não ousei deixá-lo funcionar por mais de ~ 10min.{
}
estão entre'
'
aspas ; a concha os passa inalteradosgrep
. Enfim,{1,9999}
seria uma expansão de chave muito rápida e simples . O shell iria apenas expandi-lo para1 9999
.ps
etop
para verificar segrep
foram transmitidos os argumentos esperados e se ele nãobash
consome muita RAM e CPU. Eu esperogrep
esed
ambos usam as funções de regex POSIX implementadas em libc para correspondência BRE / ERE; Eu realmente não deveria ter falado sobregrep
design especificamente, exceto na medida em que osgrep
desenvolvedores optaram por usar essa biblioteca.time grep ... < /dev/null
, para que as pessoas não confundam o problema real com os dados alimentadosgrep
e outras coisas estranhas.Respostas:
Observe que não é a correspondência que leva tempo, mas a construção do ER. Você verá que ele usa bastante RAM também:
O número de alocações parece aproximadamente proporcional ao número de iterações, mas a memória alocada parece crescer exponencialmente.
Isso se deve à maneira como os regexps GNU são implementados. Se você compilar GNU
grep
comCPPFLAGS=-DDEBUG ./configure && make
, e executar os comandos, você verá o efeito exponencial em ação. Aprofundar isso significaria passar por muita teoria sobre o DFA e mergulhar na implementação do gnulib regexp.Aqui, você pode usar PCREs que parecem não ter o mesmo problema:
grep -Po '[0-9]{1,65535}'
(o máximo, embora você sempre possa fazer coisas como[0-9](?:[0-9]{0,10000}){100}
1 a 1.000.001 repetições) não leva mais tempo nem memória do quegrep -Po '[0-9]{1,2}'
.fonte
grep -Po '[0-9]{1,9999}
que parece não ter o problema.sed -E
ougrep -E
, mas emawk
também tem esse baixo desempenho (cerca último comando awk). talvezawk
também não possa usar o PCRE?