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, ci-dessous sont appliqués quelques tests. [1] [2]
- La performance relative des
grep -E
,egrep
etsed -E
est à peu près égale, de sorte que le test qui ont été faites avecgrep -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 > vrai 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 performance?
command-line
grep
regex
pa4080
la source
la source
[0-9]+
)time grep -E '[0-9]{1,99}' </dev/null
contretime grep -E '[0-9]{1,9999}' </dev/null
. Même sans entrée , la deuxième commande est lente (le 16.04). Comme prévu, omettre-E
et s'échapper{
et}
se comporte de la même manière et le remplacer-E
par-P
n'est pas lent (PCRE est un moteur différent). Le plus intéressant est combien plus rapide[0-9]
est que.
,x
et même[0123456789]
. Avec l'un de ceux-ci et{1,9999}
,grep
consomme une énorme quantité de RAM; Je n'ai pas osé le laisser fonctionner pendant plus de ~ 10min.{
}
sont'
'
cités ; l'obus les passe inchangésgrep
. Quoi qu'il en soit, ce{1,9999}
serait une expansion d'accolade très rapide et simple . Le shell ne ferait que l'étendre1 9999
.ps
ettop
pour vérifier quegrep
les arguments attendus étaient passés et que cela nebash
consommait pas beaucoup de RAM et de CPU. J'attendsgrep
et lessed
deux utilisent les fonctions d'expression régulière POSIX implémentées dans libc pour la correspondance BRE / ERE; Je n'aurais pas vraiment dû parler degrep
design spécifiquement, sauf dans la mesure où lesgrep
développeurs ont choisi d'utiliser cette bibliothèque.time grep ... < /dev/null
, afin que les gens ne confondent pas le problème réel avec les données alimentéesgrep
et d'autres choses étrangères.Réponses:
Notez que ce n'est pas l'appariement qui prend du temps, mais la construction du RE. Vous constaterez qu'il utilise également beaucoup de RAM:
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 les expressions rationnelles GNU sont implémentées. Si vous compilez GNU
grep
avecCPPFLAGS=-DDEBUG ./configure && make
et exécutez ces commandes, vous verrez l'effet exponentiel en action. Aller plus loin que cela signifierait passer en revue beaucoup de théorie sur DFA et plonger dans l'implémentation de l'expression rationnelle de gnulib.Ici, vous pouvez utiliser des PCRE à la place qui ne semblent pas avoir le même problème:
grep -Po '[0-9]{1,65535}'
(le maximum, bien que vous puissiez toujours faire des choses comme[0-9](?:[0-9]{0,10000}){100}
pour 1 à 1 000 001 répétitions) ne prend pas plus de temps ni de mémoire quegrep -Po '[0-9]{1,2}'
.la source
grep -Po '[0-9]{1,9999}
ce qui ne semble pas avoir le problème.sed -E
ougrep -E
, mais enawk
a aussi ce faible rendement (environ la dernière commande awk). peut-êtreawk
ne peut-il pas non plus utiliser PCRE?