Grep -E, Sed -E - basse prestazioni quando si usa '[x] {1.9999}', ma perché?


9

Quando grepo sedvengono utilizzati con l'opzione --extended-regexpe il modello {1,9999}fa parte della regexp utilizzata, le prestazioni di questi comandi diminuiscono. Per essere più chiari, di seguito vengono applicati alcuni test. [1] [2]

  • Le prestazioni relative di grep -E, egreped sed -Eè quasi uguale, quindi grep -Evengono forniti solo i test effettuati .

Test 1

$ time grep -E '[0-9]{1,99}' < /dev/null

real    0m0.002s

Test 2

$ time grep -E '[0-9]{1,9999}' < /dev/null

> real    0m0.494s

Test 3

$ time grep -E '[0123456789] {1.9999}' </ dev / null

> 21m43.947 reali

Test 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 è la ragione di questa significativa differenza nelle prestazioni?


3
Questa è un'osservazione interessante - suppongo che dovresti scavare a fondo negli interni di grep per scoprire esattamente come sta costruendo l'albero di analisi (sarebbe interessante anche confrontare [0-9]+)
steeldriver

3
L'input non ha importanza. Come suggerisce @steeldriver, il rallentamento precede la corrispondenza. Un test più semplice è time grep -E '[0-9]{1,99}' </dev/nullcontro time grep -E '[0-9]{1,9999}' </dev/null. Anche senza input , il secondo comando è lento (su 16.04). Come previsto, omettere -Ee fuggire {e }comportarsi allo stesso modo e sostituirsi -Econ -Pnon è lento (PCRE è un motore diverso). Più interessante è quanto più veloce [0-9] è più ., xe persino [0123456789]. Con uno di questi e {1,9999}, grepconsuma un'enorme quantità di RAM; Non ho osato lasciarlo funzionare per più di ~ 10min.
Eliah Kagan,

3
@ αғsнιη No, { }sono ' 'citati ; il guscio li passa invariato a grep. Comunque, {1,9999}sarebbe un'espansione molto veloce e semplice . La shell lo espanderebbe a 1 9999.
Eliah Kagan,

4
@ αғsнιη Non so bene cosa intendi, ma questo non ha assolutamente nulla a che fare con la shell. Durante un comando di lunga durata, ho usato pse topper verificare che grepgli argomenti previsti fossero passati e che non bashconsumassero molta RAM e CPU. Mi aspetto greped sedentrambi uso le funzioni regex POSIX implementate in libc per la corrispondenza BRE / ERE; Non avrei dovuto parlare del grepdesign in modo specifico, tranne nella misura in cui gli grepsviluppatori hanno scelto di utilizzare quella libreria.
Eliah Kagan,

3
Ti suggerisco di sostituire i test con time grep ... < /dev/null, in modo che le persone non confondano il problema reale con i dati forniti grepe altre cose estranee.
muru,

Risposte:


10

Si noti che non è la corrispondenza che richiede tempo, ma la costruzione di RE. Scoprirai che utilizza anche molta RAM:

$ valgrind grep -Eo '[0-9]{1,9999}' < /dev/null
==6518== HEAP SUMMARY:
==6518==     in use at exit: 1,603,530,656 bytes in 60,013 blocks
==6518==   total heap usage: 123,613 allocs, 63,600 frees, 1,612,381,621 bytes allocated
$ valgrind grep -Eo '[0-9]{1,99}' < /dev/null
==6578==     in use at exit: 242,028 bytes in 613 blocks
==6578==   total heap usage: 1,459 allocs, 846 frees, 362,387 bytes allocated
$ valgrind grep -Eo '[0-9]{1,999}' < /dev/null
==6594== HEAP SUMMARY:
==6594==     in use at exit: 16,429,496 bytes in 6,013 blocks
==6594==   total heap usage: 12,586 allocs, 6,573 frees, 17,378,572 bytes allocated

Il numero di alloc sembra approssimativamente proporzionale al numero di iterazioni, ma la memoria allocata sembra crescere esponenzialmente.

Questo dipende da come vengono implementate le regexps GNU. Se si compila GNU grepcon CPPFLAGS=-DDEBUG ./configure && make, ed eseguire questi comandi, si vedrà l'effetto esponenziale in azione. Andare più in profondità significherebbe passare attraverso molta teoria su DFA e tuffarsi nell'implementazione di regexp di gnulib.

Qui, invece, puoi utilizzare PCRE che non sembrano avere lo stesso problema: grep -Po '[0-9]{1,65535}'(il massimo, anche se puoi sempre fare cose come [0-9](?:[0-9]{0,10000}){100}da 1 a 1.000.001 ripetizioni) non richiede più tempo né memoria di grep -Po '[0-9]{1,2}'.


C'è un modo per aggirare questo?
Sergiy Kolodyazhnyy,

3
@SergiyKolodyazhnyy, puoi usare grep -Po '[0-9]{1,9999}quale non sembra avere il problema.
Stéphane Chazelas,

1
E 'non solo in sed -Eo grep -E, ma in awkha anche questo basso rendimento (circa ultimo comando awk). forse awknon puoi usare PCRE?
αғsнιη,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.