Dlaczego egrep [wW] [oO] [rR] [dD] jest szybszy niż słowo grep -i?

49

Używam grep -iczęściej i odkryłem, że jest wolniejszy niż jego egrepodpowiednik, gdzie dopasowuję się do dużej lub małej litery każdej litery:

$ 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

Czy grep -iwykonuje dodatkowe testy, które egrepnie?

tylda
źródło
12
Spróbuj grepodwrotnie, aby upewnić się, że nie mierzysz różnicy między buforowaniem dysku w pliku Flie.
EightBitTony
3
Plik został grepowany przed testem, więc jest buforowany. Niemal w tym samym czasie, jeśli zrobiono to w odwrotnej kolejności.
tildearrow
21
Może to zależeć od ustawień regionalnych: niektóre ustawienia regionalne wymagają skomplikowanych obliczeń, aby uwzględnić niewrażliwość na wielkość liter. GNU grep jest szczególnie wolny w wielu sytuacjach związanych z Unicode. Z jakich ustawień regionalnych korzystałeś? Pod jakim wariantem Uniksa? Jaka jest zawartość twojego pliku testowego?
Gilles „SO- przestań być zły”
6
@Gilles wygląda dobrze, powtarzając tutaj każdy test 100 razy (cały czas), egrepjest szybszy niż grepdo momentu ustawienia, LANG=Ca potem oba są mniej więcej takie same.
EightBitTony
2
@EightBitTony Spójrz na userczas (który nie obejmuje czasu oczekiwania na dysk). Różnica jest rzędu wielkości.
kasperd

Odpowiedzi:

70

grep -i 'a'jest równoważne z grep '[Aa]'ustawieniami regionalnymi tylko ASCII. W ustawieniach regionalnych Unicode ekwiwalenty znaków i konwersje mogą być złożone, więc grepmoże być konieczne wykonanie dodatkowej pracy w celu ustalenia, które znaki są równoważne. Odpowiednie ustawienie regionalne LC_CTYPEokreśla, w jaki sposób bajty są interpretowane jako znaki.

Z mojego doświadczenia wynika, że ​​GNU grepmoże działać wolno, gdy jest wywoływany w ustawieniach regionalnych UTF-8. Jeśli wiesz, że szukasz tylko znaków ASCII, wywoływanie go w ustawieniach tylko ASCII może być szybsze. oczekuję tego

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

wytworzyłyby nierozróżnialne czasy.

To powiedziawszy, nie mogę odtworzyć twojego odkrycia z GNU grepna Debianie jessie (ale nie określiłeś pliku testowego). Jeśli ustawię ustawienia regionalne ASCII ( LC_ALL=C), grep -ijest to szybsze. Efekty zależą od dokładnej natury łańcucha, na przykład łańcuch z powtarzającymi się znakami zmniejsza wydajność ( czego należy się spodziewać ).

Gilles „SO- przestań być zły”
źródło
Autor używa Ubuntu 14.04, który jest dostarczany z grep 2.10. Szybkość dopasowywania bez rozróżniania wielkości liter ( -i) w ustawieniach wielobajtowych powinna poprawić się w 2.17 .
Lekensteyn,
@Lekensteyn Dobrze wiedzieć, dziękuję. Ubuntu 14.04 faktycznie jest wyposażony w grep 2.16, ale to też jest wersja wcześniejsza niż 2.17; Testowałem z grep 2.20, co wyjaśnia, dlaczego nie widziałem tego samego spowolnienia.
Gilles „SO- przestań być zły”
Tak, patrzyłem na niewłaściwe wydanie LTS, Ubuntu 12.04 jest dostarczane z grep 2.10, podczas gdy Ubuntu 14.04 zawiera grep 2.16.
Lekensteyn,
1
Jestem pewien, że grep -i 'a'jest to równoważne z grep '[Aa]'dowolnym ustawieniem narodowym. Właściwym przykładem jest grep -i 'i'albo grep '[Ii]'albo grep '[İi]'(wielkie litery I z kropką powyżej, U + 130, ustawienia regionalne tureckie). Jednak nie ma skutecznego sposobu na grepznalezienie tej klasy równoważności w przypadku ustawień regionalnych.
MSalters
15

Z ciekawości przetestowałem to na systemie 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>.

A potem niektóre statystyki dzięki uprzejmości Czy istnieje sposób, aby uzyskać minimalną, maksymalną, średnią i średnią z listy liczb w jednym poleceniu? :

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

Jestem na en_GB.utf8miejscu, ale czasy są prawie nie do odróżnienia.

muru
źródło