web-dev-qa-db-fra.com

Grep -E, Sed -E - performances médiocres lorsque '[x] {1,9999}' est utilisé, mais pourquoi?

Lorsque grep ou sed sont utilisés avec l'option _--extended-regexp_ et que le modèle _{1,9999}_ fait partie de l'expression rationnelle utilisée, les performances de ces commandes deviennent faibles. Pour être plus clair, vous trouverez ci-dessous quelques tests . [1][2]

  • La performance relative de _grep -E_, egrep et _sed -E_ est presque égale, de sorte que seuls les tests effectués avec grep -E sont fournis.

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[.____.HERH> Réel 21m43.947s

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       
_

Quelle est la raison de cette différence significative de la performance?

8
pa4080

Notez que ce n'est pas le jumelage qui prend du temps, mais la construction de l'ER. Vous constaterez qu'il utilise beaucoup de 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

Le nombre d'allocations semble à peu près proportionnel au nombre d'itérations, mais la mémoire allouée semble croître de façon exponentielle.

Cela dépend de la façon dont GNU regexps sont implémentés. Si vous compilez GNU grep avec CPPFLAGS=-DDEBUG ./configure && make et exécutez ces commandes, vous verrez l'effet exponentiel en action. Pour aller plus loin, cela impliquerait de passer par beaucoup de théorie sur DFA et de plonger dans la mise en œuvre de regexp gnulib.

Ici, vous pouvez utiliser des PCRE qui ne semblent pas avoir le même problème: grep -Po '[0-9]{1,65535}' (maximum, bien que vous puissiez toujours faire des choses comme [0-9](?:[0-9]{0,10000}){100} pendant 1 à 1 000 001 répétitions) n'en prend pas plus. temps ni mémoire que grep -Po '[0-9]{1,2}'.

8
Stéphane Chazelas