Warum ist egrep [wW] [oO] [rR] [dD] schneller als grep -i?

49

Ich habe grep -iöfter verwendet und festgestellt, dass es langsamer als das egrepÄquivalent ist, bei dem ich die Groß- oder Kleinschreibung jedes Buchstabens vergleiche:

$ 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

Führt grep -izusätzliche Tests durch, egrepdie nicht durchgeführt werden?

Tildearrow
quelle
12
Versuchen grepSie es anders herum, um sicherzustellen, dass Sie nicht den Unterschied zwischen dem Disk-Caching des Flies messen.
EightBitTony
3
Ich habe die Datei vor dem Testen überprüft, sodass sie zwischengespeichert wird. Fast gleich mal in umgekehrter Reihenfolge.
Tildearrow
21
Dies kann vom Gebietsschema abhängen: Einige Gebietsschemas erfordern komplexe Berechnungen, um Groß- und Kleinschreibung zu berücksichtigen. GNU grep ist in vielen Situationen mit Unicode besonders langsam. Welche Gebietsschemaeinstellungen haben Sie verwendet? Unter welcher Unix-Variante? Was ist der Inhalt Ihrer Testdatei?
Gilles 'SO- hör auf böse zu sein'
6
@Gilles sieht gut aus, wenn man hier jeden Test 100 Mal wiederholt (Timing der gesamten Sache), egrepist das schneller als grepbis ich es eingestellt habe LANG=Cund dann sind beide ungefähr gleich.
EightBitTony
2
@EightBitTony Schauen Sie sich die userZeit an (ohne Wartezeit auf die Festplatte). Es gibt eine Größenordnung in der Differenz.
Kasperd

Antworten:

70

grep -i 'a'entspricht grep '[Aa]'einem Nur-ASCII-Gebietsschema. In einem Unicode-Gebietsschema können Zeichenäquivalenzen und -konvertierungen komplex sein. Daher muss grepmöglicherweise zusätzliche Arbeit geleistet werden, um festzustellen, welche Zeichen äquivalent sind. Die relevante Ländereinstellung LC_CTYPEbestimmt, wie Bytes als Zeichen interpretiert werden.

Nach meiner Erfahrung kann GNU greplangsam sein, wenn es in einem UTF-8-Gebietsschema aufgerufen wird. Wenn Sie wissen, dass Sie nur nach ASCII-Zeichen suchen, ist das Aufrufen in einem Nur-ASCII-Gebietsschema möglicherweise schneller. Das erwarte ich

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

würde zu ununterscheidbaren Zeiten führen.

grepAbgesehen davon kann ich Ihr Ergebnis mit GNU auf Debian jessie nicht reproduzieren (aber Sie haben Ihre Testdatei nicht angegeben). Wenn ich ein ASCII-Gebietsschema ( LC_ALL=C) einstelle , grep -iist das schneller. Die Auswirkungen hängen von der genauen Art der Zeichenfolge ab. Beispielsweise verringert eine Zeichenfolge mit wiederholten Zeichen die Leistung ( was zu erwarten ist ).

Gilles 'SO - hör auf böse zu sein'
quelle
Der Autor verwendet Ubuntu 14.04, das mit grep 2.10 ausgeliefert wird. Die Geschwindigkeit von Übereinstimmungen ohne -iBerücksichtigung der Groß- und Kleinschreibung ( ) mit Multibyte-Gebietsschemata sollte sich in 2.17 verbessert haben .
Lekensteyn
@Lekensteyn Gut zu wissen, danke. Ubuntu 14.04 kommt eigentlich mit grep 2.16, aber das ist auch vor 2.17; Ich habe mit grep 2.20 getestet, was erklärt, warum ich nicht die gleiche Verlangsamung gesehen habe.
Gilles 'SO - hör auf böse zu sein'
Richtig, ich habe mir die falsche LTS-Version angesehen, Ubuntu 12.04 wird mit grep 2.10 ausgeliefert, während Ubuntu 14.04 grep 2.16 enthält.
Lekensteyn
1
Ich bin mir ziemlich sicher, dass grep -i 'a'dies grep '[Aa]'in jedem Gebietsschema gleichwertig ist . Das richtige Beispiel ist grep -i 'i'entweder grep '[Ii]'oder grep '[İi]'(Großbuchstabe I mit Punkt oben, U + 130, türkisches Gebietsschema). Es gibt jedoch keine effiziente Möglichkeit grep, diese Äquivalenzklasse für ein bestimmtes Gebietsschema zu finden.
MSalters
15

Aus Neugier habe ich dies auf einem Arch Linux-System getestet:

$ 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>.

Und dann einige Statistiken mit freundlicher Genehmigung von Gibt es eine Möglichkeit, den Min, Max, Median und Durchschnitt einer Zahlenliste in einem einzigen Befehl zu ermitteln? :

$ 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
> 
> 

Ich bin auf dem en_GB.utf8Gebietsschema, aber die Zeiten sind fast nicht zu unterscheiden.

muru
quelle