it-swarm.com.de

Grep -E, Sed -E - geringe Leistung, wenn '[x] {1,9999}' verwendet wird, aber warum?

Wenn grep oder sed mit der Option _--extended-regexp_ verwendet werden und das Muster _{1,9999}_ Teil des verwendeten regulären Ausdrucks ist, ist die Leistung dieser Befehle gering. Um es klarer zu machen, werden im Folgenden einige Tests angewendet. [1][2]

  • Die relative Leistung von _grep -E_, egrep und _sed -E_ ist nahezu gleich, daher nur der Test, der mit grep -E sind vorhanden.

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}'> echte 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       
_

Was ist der Grund für diesen signifikanten Unterschied in der Leistung?

8
pa4080

Beachten Sie, dass nicht das Matching Zeit kostet, sondern der Aufbau des RE. Sie werden feststellen, dass es ziemlich viele RAM verwendet:

$ 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

Die Anzahl der Zuweisungen scheint in etwa proportional zur Anzahl der Iterationen zu sein, aber der zugewiesene Speicher scheint exponentiell zu wachsen.

Das hängt davon ab, wie GNU reguläre Ausdrücke implementiert werden. Wenn Sie GNU grep mit CPPFLAGS=-DDEBUG ./configure && make kompilieren und diese Befehle ausführen, sehen Sie den Exponentialeffekt in Aktion. Tiefer zu gehen, würde bedeuten, eine Menge Theorie über DFA durchzugehen und in die Implementierung von Gnulib-Regexp einzutauchen.

Hier können Sie stattdessen PCREs verwenden, die nicht dasselbe Problem zu haben scheinen: grep -Po '[0-9]{1,65535}' (das Maximum, obwohl Sie immer Dinge wie [0-9](?:[0-9]{0,10000}){100} für 1 bis 1.000.001 Wiederholungen ausführen können) nimmt nicht mehr in Anspruch Zeit noch Speicher als grep -Po '[0-9]{1,2}'.

8