Pourquoi egrep [wW] [oO] [rR] [dD] est-il plus rapide que grep -i word?

49

J'ai utilisé grep -iplus souvent et j'ai découvert qu'il est plus lent que son egrepéquivalent, où je fais correspondre le caractère majuscule ou minuscule de chaque lettre:

$ time grep -iq "thats" testfile

real    0m0.041s
user    0m0.038s
sys     0m0.003s
$ time egrep -q "[tT][hH][aA][tT][sS]" testfile

real    0m0.010s
user    0m0.003s
sys     0m0.006s

Est grep -i- ce que des tests supplémentaires egrepne le font pas?

tildearrow
la source
12
Essayez l grep'inverse, pour vous assurer que vous ne mesurez pas la différence entre la mise en cache disque du fichier.
EightBitTony
3
J'ai grep'd le fichier avant de tester, donc il est mis en cache. Presque même temps si c'est fait dans l'ordre inverse.
Tildearrow
21
Cela peut dépendre des paramètres régionaux: certains paramètres régionaux impliquent des calculs complexes pour tenir compte de l'insensibilité à la casse. GNU grep est particulièrement lent dans de nombreuses situations impliquant Unicode. Quels paramètres régionaux avez-vous utilisés? Sous quelle variante Unix? Quel est le contenu de votre fichier de test?
Gilles 'SO- arrête d'être méchant'
6
@Gilles a l'air bien, en répétant chaque test ici 100 fois (en chronométrant le tout), egrepc'est plus rapide que grepjusqu'à ce que je règle LANG=C, puis les deux sont à peu près les mêmes.
EightBitTony
2
@EightBitTony Regardez l' userheure (qui n'inclut pas le temps d'attente pour le disque). Il y a un ordre de grandeur dans la différence.
Kasperd

Réponses:

70

grep -i 'a'est équivalent à grep '[Aa]'dans un environnement local uniquement ASCII. Dans les paramètres régionaux Unicode, les équivalences et les conversions de caractères peuvent être complexes. Il greppeut donc être nécessaire d'effectuer un travail supplémentaire pour déterminer quels caractères sont équivalents. Le paramètre régional approprié est LC_CTYPE: il détermine comment les octets sont interprétés en tant que caractères.

D'après mon expérience, GNU greppeut être lent lorsqu'il est appelé dans un environnement local UTF-8. Si vous savez que vous recherchez uniquement des caractères ASCII, il peut être plus rapide de l'appeler dans des paramètres régionaux uniquement. J'attends cela

time LC_ALL=C grep -iq "thats" testfile
time LC_ALL=C egrep -q "[tT][hH][aA][tT][sS]" testfile

produirait des horaires impossibles à distinguer.

Cela étant dit, je ne peux pas reproduire votre résultat avec GNU grepsur Debian Jessie (mais vous n'avez pas spécifié votre fichier de test). Si je définis une locale ASCII ( LC_ALL=C), grep -iest plus rapide. Les effets dépendent de la nature exacte de la chaîne. Par exemple, une chaîne contenant des caractères répétés réduit les performances ( ce qui est prévisible ).

Gilles, arrête de faire le mal
la source
L'auteur utilise Ubuntu 14.04, livré avec grep 2.10. La vitesse des correspondances ( -i) insensibles à la casse ( ) avec des environnements locaux multi-octets aurait dû être améliorée dans 2.17 .
Lekensteyn
@Lekensteyn Bon à savoir, merci. En réalité, Ubuntu 14.04 est fourni avec grep 2.16, mais c'est également une version antérieure à la version 2.17; J'ai testé avec grep 2.20, ce qui explique pourquoi je n'ai pas vu le même ralentissement.
Gilles 'SO- arrête d'être méchant'
En effet, je cherchais la mauvaise version de LTS. Ubuntu 12.04 est livré avec grep 2.10, tandis qu’Ubuntu 14.04 inclut grep 2.16.
Lekensteyn
1
Je suis presque certain que cela grep -i 'a'équivaut à grep '[Aa]'n'importe quel endroit Le bon exemple est celui grep -i 'i'qui est grep '[Ii]'ou grep '[İi]'(majuscule I avec un point au-dessus, U + 130, langue turque). Cependant, il n'existe aucun moyen efficace grepde trouver cette classe d'équivalence en fonction des paramètres régionaux.
MSalters
15

Par curiosité, j'ai testé cela sur un système Arch Linux:

$ uname -r
4.4.5-1-ARCH
$ df -h .
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           3.9G  720K  3.9G   1% /tmp
$ dd if=/dev/urandom bs=1M count=1K | base64 > foo
$ df -h .                                         
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           3.9G  1.4G  2.6G  35% /tmp
$ for i in {1..100}; do /usr/bin/time -f '%e' -ao grep.log grep -iq foobar foo; done
$ for i in {1..100}; do /usr/bin/time -f '%e' -ao egrep.log egrep -q '[fF][oO][oO][bB][aA][rR]' foo; done

$ grep --version
grep (GNU grep) 2.23
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

Et puis quelques statistiques de courtoisie de Y at-il un moyen d’obtenir le minimum, le maximum, la médiane et la moyenne d’une liste de nombres dans une seule commande? :

$ R -q -e "x <- read.csv('grep.log', header = F); summary(x); sd(x[ , 1])"
> x <- read.csv('grep.log', header = F); summary(x); sd(x[ , 1])
       V1       
 Min.   :1.330  
 1st Qu.:1.347  
 Median :1.360  
 Mean   :1.362  
 3rd Qu.:1.370  
 Max.   :1.440  
[1] 0.02322725
> 
> 
$ R -q -e "x <- read.csv('egrep.log', header = F); summary(x); sd(x[ , 1])"
> x <- read.csv('egrep.log', header = F); summary(x); sd(x[ , 1])
       V1       
 Min.   :1.330  
 1st Qu.:1.340  
 Median :1.360  
 Mean   :1.365  
 3rd Qu.:1.380  
 Max.   :1.430  
[1] 0.02320288
> 
> 

Je suis sur place en_GB.utf8, mais les temps sont presque indiscernables.

muru
la source