Cuando grep
o sed
se usan con la opción --extended-regexp
y el patrón {1,9999}
es una parte de la expresión regular que se usa, el rendimiento de estos comandos se vuelve bajo. Para ser más claro, a continuación se aplican algunas pruebas. [1] [2]
- El rendimiento relativo de
grep -E
,egrep
ysed -E
es casi igual, por lo que solo se proporcionan las pruebas realizadasgrep -E
.
Prueba 1
$ time grep -E '[0-9]{1,99}' < /dev/null
real 0m0.002s
Prueba 2
$ time grep -E '[0-9]{1,9999}' < /dev/null
> real 0m0.494s
Prueba 3
$ time grep -E '[0123456789] {1,9999}' </ dev / null > real 21m43.947s
Prueba 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
¿Cuál es la razón de esta diferencia significativa de rendimiento?
command-line
grep
regex
pa4080
fuente
fuente
[0-9]+
también)time grep -E '[0-9]{1,99}' </dev/null
vstime grep -E '[0-9]{1,9999}' </dev/null
. Incluso sin entrada , el segundo comando es lento (en 16.04). Como se esperaba, omitir-E
y escapar{
y se}
comporta igual y reemplazar-E
con-P
no es lento (PCRE es un motor diferente). Lo más interesante es cuánto más rápido[0-9]
es que.
,x
y hasta[0123456789]
. Con cualquiera de esos y{1,9999}
,grep
consume una gran cantidad de RAM; No me he atrevido a dejarlo correr por más de ~ 10 minutos.{
}
se'
'
citan ; el caparazón los pasa sin cambiosgrep
. De todos modos,{1,9999}
sería una expansión de llaves muy rápida y simple . El shell simplemente lo expandiría a1 9999
.ps
ytop
para verificargrep
se pasaron los argumentos esperados y que, nobash
, consume mucha RAM y CPU. Esperogrep
ysed
ambos usan las funciones de expresiones regulares POSIX implementadas en libc para la coincidencia BRE / ERE; Realmente no debería haber habladogrep
específicamente del diseño, excepto en la medida en que losgrep
desarrolladores eligieron usar esa biblioteca.time grep ... < /dev/null
, para que las personas no combinen el problema real con los datos que se envíangrep
y otras cosas extrañas.Respuestas:
Tenga en cuenta que no es la correspondencia lo que lleva tiempo, sino la construcción del RE. Encontrarás que también usa bastante RAM:
El número de asignaciones parece aproximadamente proporcional al número de iteraciones, pero la memoria asignada parece crecer exponencialmente.
Eso se debe a cómo se implementan las expresiones regulares GNU. Si compila GNU
grep
conCPPFLAGS=-DDEBUG ./configure && make
y ejecuta esos comandos, verá el efecto exponencial en acción. Ir más profundo que eso significaría pasar por muchas teorías sobre DFA y sumergirse en la implementación de expresiones regulares de gnulib.Aquí, puede usar PCRE en su lugar que no parece tener el mismo problema:
grep -Po '[0-9]{1,65535}'
(el máximo, aunque siempre puede hacer cosas como[0-9](?:[0-9]{0,10000}){100}
1 a 1,000,001 repeticiones) no toma más tiempo ni memoria quegrep -Po '[0-9]{1,2}'
.fuente
grep -Po '[0-9]{1,9999}
lo que no parece tener el problema.sed -E
ogrep -E
, pero en laawk
que también tiene este bajo rendimiento (alrededor del último comando awk). tal vezawk
tampoco puede usar PCRE?