Почему egrep [wW] [oO] [rR] [dD] быстрее, чем слово grep -i?

49

Я использовал grep -iчаще и обнаружил, что это медленнее, чем его egrepэквивалент, где я сопоставляю верхний или нижний регистр каждой буквы:

$ 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

Есть grep -iли дополнительные тесты, которые egrepне делают?

tildearrow
источник
12
Попробуйте grepвсе наоборот, чтобы убедиться, что вы не измеряете разницу между кэшированием диска мухи.
EightBitTony
3
У меня есть grep'd файл до тестирования, поэтому он кэшируется. Почти то же самое время, если сделано в обратном порядке.
tildearrow
21
Это может зависеть от локали: некоторые локали включают сложные вычисления для учета нечувствительности к регистру. GNU grep особенно медленен во многих ситуациях, связанных с Юникодом. Какие настройки локали вы использовали? Под какой Unix вариант? Каково содержание вашего тестового файла?
Жиль "ТАК - перестань быть злым"
6
@ Жиль выглядит хорошо, повторяя каждый тест здесь 100 раз (рассчитывая время целиком), egrepбыстрее, чем grepдо того, как я установлю, LANG=Cи тогда они оба примерно одинаковы.
EightBitTony
2
@EightBitTony Посмотрите на userвремя (которое не включает время ожидания диска). Существует разность порядка.
Касперд

Ответы:

70

grep -i 'a'эквивалентно grep '[Aa]'в ASCII-только локали. В локали Unicode эквивалентности и преобразования символов могут быть сложными, поэтому grepможет потребоваться дополнительная работа, чтобы определить, какие символы эквивалентны. Соответствующий параметр локали LC_CTYPE, который определяет, как байты интерпретируются как символы.

По моему опыту, GNU grepможет быть медленным при вызове в локали UTF-8. Если вы знаете, что ищете только символы ASCII, то его вызов в локали только для ASCII может быть быстрее. Я ожидаю что

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

будет производить неразличимые сроки.

При этом я не могу воспроизвести ваши находки с помощью GNU grepдля Debian jessie (но вы не указали свой тестовый файл). Если я установил ASCII locale ( LC_ALL=C), grep -iэто быстрее. Эффекты зависят от точного характера строки, например, строка с повторяющимися символами снижает производительность ( что и следовало ожидать ).

Жиль "ТАК - перестань быть злым"
источник
Автор использует Ubuntu 14.04, которая поставляется с grep 2.10. Скорость нечувствительных к регистру совпадений ( -i) с многобайтовыми локалями должна была улучшиться в 2.17 .
Лекенштейн
@Lekensteyn Полезно знать, спасибо. Ubuntu 14.04 на самом деле поставляется с grep 2.16, но это тоже до 2.17; Я протестировал с grep 2.20, который объясняет, почему я не видел такое же замедление.
Жиль "ТАК - перестань быть злым"
Да, я смотрел неправильную версию LTS: Ubuntu 12.04 поставляется с grep 2.10, а Ubuntu 14.04 включает в себя grep 2.16.
Лекенштейн
1
Я совершенно уверен, что grep -i 'a'это эквивалентно grep '[Aa]'любой локали. Правильный пример - grep -i 'i'либо grep '[Ii]'или grep '[İi]'(Прописная буква I с точкой выше, U + 130, турецкая локаль). Однако эффективного способа grepнайти этот класс эквивалентности для данной локали не существует.
MSalters
15

Из любопытства я проверил это в системе 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>.

А затем некоторые статистические данные любезно предоставлены. Есть ли способ получить минимальное, максимальное, среднее значение и среднее для списка чисел в одной команде? :

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

Я на en_GB.utf8месте, но времена почти неразличимы.

Мур
источник