1

Может кому будет интересно, простые выборки из огромного файла на 13 млн. строк:

valet@valet-comp:~/Documents/all$ wc -l all
13107193 all
valet@valet-comp:~/Documents/all$ time awk '/foo/ { print }' all > /dev/null 

real    0m16.518s
user    0m16.381s
sys    0m0.128s
valet@valet-comp:~/Documents/all$ time sed -n '/foo/p' all > /dev/null 

real    0m2.959s
user    0m2.888s
sys    0m0.064s
valet@valet-comp:~/Documents/all$ time grep 'foo' all > /dev/null 

real    0m0.872s
user    0m0.788s
sys    0m0.080s
valet@valet-comp:~/Documents/all$ time perl -ne 'print if /foo/' all > /dev/null 

real    0m3.303s
user    0m3.260s
sys    0m0.036s
valet@valet-comp:~/Documents/all$ time grep 'foo' all > /dev/null 

real    0m0.878s
user    0m0.800s
sys    0m0.076s
valet@valet-comp:~/Documents/all$ time perl -ne 'print if /foo/' all > /dev/null 

real    0m3.284s
user    0m3.180s
sys    0m0.100s

По получается такой топ по скорости:

1. grep
2. sed
3. perl
4. awk

Честно говоря, меня сильно удивили эти тесты (повторял много раз - результаты те же).

1. Почему grep оказался шустрее всех - я думал он наоборот самый медленный - всегда даже старался использовать sed -n для выборок.
2. Как же так получается, что perl медленнее чем sed и grep? Я даже специально повторил несколько раз подряд тесты с grep и perl - так как не поверил свои глазам ab
3. awk совсем разочаровал - что он там такое делает что так долго отрабатывает ab

Я конечно понимаю, что сравнение этих инструментов может не совсем корректное, так как они могут выполнять разный по сложности функционал, но вот на таких простых выборках с огромного файла результаты именно такие.

2

Проверить бы еще Python но не могу чето нагулить как сделать выборку строки из файла.

3

valet пишет:

Я конечно понимаю, что сравнение этих инструментов может не совсем корректное, так как они могут выполнять разный по сложности функционал

Вы сами ответили.

Был, был и весь вышел...

4

valet пишет:

Как же так получается, что perl медленнее чем sed и grep?

perl - интерпретируемый язык, а sed и grep написаны на Си. Хотя, многое ещё от алгоритма зависит.

Да, у меня Ubuntu.
Нет, не глючит.

5

Lupo Alberto пишет:

Вы сами ответили.

Ну вообще-то сравнение шло в контексте вот такой вот тривиальной задачи как выборка - то что мы как порядочные линуксоиды ab сотни раз в сутки используем из консоли.

6

valet пишет:

Проверить бы еще Python но не могу чето нагулить как сделать выборку строки из файла.

Ну, что-нибудь вроде этого:

#!/usr/bin/env python
#_*_ coding: utf-8 _*_

import sys

if len(sys.argv) != 3:
    sys.stderr.write("Usage %s <substr> <filename>\n" % sys.argv[0])
    raise SystemExit(1)

with open(sys.argv[2], 'r') as f:
    for line in f:
        if sys.argv[1] in line:
            print line,
Да, у меня Ubuntu.
Нет, не глючит.

7

-=Serj=- пишет:

Ну, что-нибудь вроде этого:

давай-ка по-честному, с регэкспами...

valet, кстати, интересно, как разбрасываются результаты в зависимости от сложности выражения... [по идее только перловка будет меньше в процентах отставать]

95% процентов проблем находятся между клавиатурой и стулом.

8 (28.02.2013 14:26:51 отредактировано -=Serj=-)

Fat-Zer пишет:

давай-ка по-честному, с регэкспами...

#!/usr/bin/env python
#_*_ coding: utf-8 _*_

import sys, re

if len(sys.argv) != 3:
    sys.stderr.write("Usage %s <substr> <filename>\n" % sys.argv[0])
    raise SystemExit(1)
    
patt = re.compile(sys.argv[1])

with open(sys.argv[2], 'r') as f:
    for line in f:
        if patt.search(line):
            print line,

Как такового регэкспа тут нет...
Или что ты имел ввиду?

Да, у меня Ubuntu.
Нет, не глючит.

9

-=Serj=- пишет:

Ну, что-нибудь вроде этого:

а что вообще делает это скрипт?
Вот я вкинул этот код

#!/usr/bin/env python
#_*_ coding: utf-8 _*_

import sys, re

if len(sys.argv) != 3:
    sys.stderr.write("Usage %s foo all\n" % sys.argv[0])
    raise SystemExit(1)
    
patt = re.compile(sys.argv[1])

with open(sys.argv[2], 'r') as f:
    for line in f:
        if patt.search(line):
            print line,

запустил и получил

Usage ./python_test foo all

real    0m0.019s
user    0m0.012s
sys    0m0.000s

Если это правда, то конечно круто. Но думаю я что-то неправильно сделал.

10

valet пишет:

запустил и получил

Не-не. В скрипте править ничего не надо. Запускайте так:

./python_test foo all
Да, у меня Ubuntu.
Нет, не глючит.

11

Ага, понял, тогда так несколько раз подряд:

valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m4.696s
user    0m4.616s
sys    0m0.072s
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m4.708s
user    0m4.616s
sys    0m0.088s
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m4.663s
user    0m4.536s
sys    0m0.120s

То есть получается только получше, чем awk.

12 (28.02.2013 15:16:45 отредактировано -=Serj=-)

valet пишет:

То есть получается только получше, чем awk.

C’est la vie ab
Могу для экспериментов ещё бинарник на Go попробовать собрать ab

ЗЫ. Для интереса, попробуйте первый вариант на Python - grep sed perl awk - что быстрее для простых выборок

Да, у меня Ubuntu.
Нет, не глючит.

13 (28.02.2013 15:36:48 отредактировано valet)

Fat-Zer пишет:

valet, кстати, интересно, как разбрасываются результаты в зависимости от сложности выражения... [по идее только перловка будет меньше в процентах отставать]

Ок, замена foo на bar сойдет?

valet@valet-comp:~/Documents/all$ time perl -p -e 's/foo/bar/g' all > /dev/null 

real    0m4.239s
user    0m4.160s
sys    0m0.072s
valet@valet-comp:~/Documents/all$ time sed 's/foo/bar/g' all > /dev/null 

real    0m3.917s
user    0m3.796s
sys    0m0.116s

sed тут опять таки почему-то быстрее perl'a, но зато perl умеет менять сразу несколько вхождений:

valet@valet-comp:~/Documents/all$ time perl -p -e 's/(foo|foo2|foo3|foon)/bar/g' all > /dev/null 

real    0m4.220s
user    0m4.100s
sys    0m0.116s

А это большой плюс, могу ошибаться, но кажется sed так не умеет - в случае sed'а пришлось бы делать отдельные замены через пайпы.

14

-=Serj=- пишет:

ЗЫ. Для интереса, попробуйте первый вариант на Python

так я и попробовал.

15

-=Serj=- пишет:

Могу для экспериментов ещё бинарник на Go попробовать собрать

попробуй, интересно ab

16

valet пишет:

так я и попробовал.

Тогда, второй - grep sed perl awk - что быстрее для простых выборок

Да, у меня Ubuntu.
Нет, не глючит.

17

Ага, не обратил внимание, что это разные скрипты. Тогда так:

valet@valet-comp:~/Documents/all$ cat python_test 
#!/usr/bin/env python
#_*_ coding: utf-8 _*_

import sys, re

if len(sys.argv) != 3:
    sys.stderr.write("Usage %s <substr> <filename>\n" % sys.argv[0])
    raise SystemExit(1)
    
patt = re.compile(sys.argv[1])

with open(sys.argv[2], 'r') as f:
    for line in f:
        if patt.search(line):
            print line,
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m4.693s
user    0m4.620s
sys    0m0.068s
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m4.689s
user    0m4.588s
sys    0m0.096s
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m4.707s
user    0m4.556s
sys    0m0.144s
valet@valet-comp:~/Documents/all$ 

и так:

valet@valet-comp:~/Documents/all$ cat python_test 
#!/usr/bin/env python
#_*_ coding: utf-8 _*_

import sys

if len(sys.argv) != 3:
    sys.stderr.write("Usage %s <substr> <filename>\n" % sys.argv[0])
    raise SystemExit(1)

with open(sys.argv[2], 'r') as f:
    for line in f:
        if sys.argv[1] in line:
            print line,
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m2.784s
user    0m2.688s
sys    0m0.092s
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m2.788s
user    0m2.696s
sys    0m0.088s
valet@valet-comp:~/Documents/all$ time ./python_test foo all > /dev/null 

real    0m2.780s
user    0m2.696s
sys    0m0.080s
valet@valet-comp:~/Documents/all$ 

То есть это уже неплохо и топ по скорости будет таким:

1. grep
2. python
3. sed
4. perl
5. awk

18 (28.02.2013 17:28:30 отредактировано -=Serj=-)

valet пишет:
-=Serj=- пишет:

Могу для экспериментов ещё бинарник на Go попробовать собрать

попробуй, интересно ab

Бинарник на GO:
http://ubuntuone.com/2lSIeO2ssKSgBUcUU2iYmU
Запускать так же:

./go-grep foo all

ЗЫ. Этот вариант должен быть по-шустрее первого: http://ubuntuone.com/2EpcfsqzWPes75UGpHrjlQ
Исходник:

package main

import (
    "bufio"
    "fmt"
    "os"
    "regexp"
)

func main() {

    if len(os.Args) != 3 {
        fmt.Printf("Usage: %s <substring> <filename>\n", os.Args[0])
        return
    }
    substr := os.Args[1]
    fname := os.Args[2]

    f, err := os.Open(fname)
    if err != nil {
        return
    }
    defer f.Close()

    r := bufio.NewReader(f)
    patt, _ := regexp.Compile(substr)

    for {
        s, ok := r.ReadString('\n')
        if ok != nil {
            break
        }
        matches := patt.FindString(s)
        if matches != "" {
            fmt.Print(s)
        }

    }

}
Да, у меня Ubuntu.
Нет, не глючит.

19

набросал тут тестовый скриптик и прогнал его на сложном выражение...
места сильно отличаются:
grep - бесспорный лидер,
perl - второе место с многократным отставанием
awk - немного недотянул до перловки
sed - на порядок отстаёт от остальных
python - плетётся в самом конце

+ результаты

 Консоль:
# тестировать будем на исходниках ядра
alexander@goblin /tmp $ find linux-3.0/ -type f -exec cat {} + | tee megafile |wc -l
14647033
# сложное выражение
alexander@goblin /tmp $ ./grep-test.sh '\s*return.*;'
wc -l for   grep: 411133
wc -l for   perl: 411133
wc -l for    awk: 411133
wc -l for    sed: 411133
wc -l for python: 411133
tests number: 10
time for   grep:   4.04 (avg  0.40)
time for   perl:  27.28 (avg  2.73)
time for    awk:  28.76 (avg  2.88)
time for    sed: 174.89 (avg 17.49)
time for python: 182.10 (avg 18.21)
# простое выражение
# все улучшили результаты, отставание аутсайдеров сократилось

wc -l for   grep: 453902
wc -l for   perl: 453902
wc -l for    awk: 453902
wc -l for    sed: 453902
wc -l for python: 453902
tests number: 10
time for   grep:   4.23 (avg  0.42)
time for   perl:  25.33 (avg  2.53)
time for    awk:  28.56 (avg  2.86)
time for    sed:  36.24 (avg  3.62)
time for python:  69.19 (avg  6.92)
# не встречающееся простое выражение
# ещё немного лучше, примечательно трёхкратное, улучшение grep'а, sed наоборот немного скатился

./grep_test.sh 'this_statment_is_false'
wc -l for   grep:0
wc -l for   perl:0
wc -l for    awk:0
wc -l for    sed:0
wc -l for python:0
tests number: 10
time for   grep:   1.60 (avg  0.16)
time for   perl:  21.87 (avg  2.19)
time for    awk:  28.30 (avg  2.83)
time for    sed:  37.31 (avg  3.73)
time for python:  68.18 (avg  6.82)
# не встречающееся сложное выражение
# без особых изменений

alexander@goblin /tmp $ ./grep_test.sh 'this.*statment.*is.*false[!?]'
wc -l for   grep:0
wc -l for   perl:0
wc -l for    awk:0
wc -l for    sed:0
wc -l for python:0
tests number: 10
time for   grep:   2.80 (avg  0.28)
time for   perl:  22.88 (avg  2.29)
time for    awk:  28.24 (avg  2.82)
time for    sed:  45.55 (avg  4.55)
time for python:  68.14 (avg  6.81)
# короткое часто встречающееся и простое выражение
# а вот и вариант, где perl проигрывает awk

alexander@goblin /tmp $ ./grep_test.sh ' '
wc -l for   grep:9869702
wc -l for   perl:9869702
wc -l for    awk:9869702
wc -l for    sed:9869702
wc -l for python:9869702
tests number: 10
time for   grep:  29.31 (avg  2.93)
time for   perl:  32.03 (avg  3.20)
time for    awk:  28.74 (avg  2.87)
time for    sed:  39.30 (avg  3.93)
time for python:  87.30 (avg  8.73)
# короткое часто встречающееся и сложное выражение
# ещё один вариант, где perl лажает... примечателен также тем, что sed не нашёл 15 строк

alexander@goblin /tmp $ ./grep_test.sh '\S'
wc -l for   grep:12654337
wc -l for   perl:12654337
wc -l for    awk:12654337
wc -l for    sed:12654324
wc -l for python:12654337
tests number: 10
time for   grep:  11.70 (avg  1.17)
time for   perl:  47.23 (avg  4.72)
time for    awk:  27.82 (avg  2.78)
time for    sed:  70.22 (avg  7.02)
time for python: 112.62 (avg 11.26)

+ grep_test.sh
#!/bin/bash

REG_EXP="$1"
FILE="/tmp/megafile"
TEST_NUM=10

PYTHON_SCRIPT="/tmp/grep.py"

SED_CMD=(sed -ne "/$REG_EXP/p")
GREP_CMD=(grep "$REG_EXP")
PERL_CMD=(perl -ne "print if /$REG_EXP/")
AWK_CMD=(awk "/$REG_EXP/ { print }")
PYTHON_CMD=(python ${PYTHON_SCRIPT} "$REG_EXP")

GNU_TIME="/usr/bin/time"

LC_ALL=C

wc_l() {
    printf "wc -l for %6s: " "${1}" 
    "$@" $FILE | wc -l
}

rtime() {
    overall=0;
    for((i=0; i<$TEST_NUM; i++)); do
        test_time="$(${GNU_TIME} -p "$@" $FILE 2>&1 1>/dev/null | sed -n '/real\s\(.*\)/{s//\1/;p}')"
        overall="$(python -c "print '%10.5f' % ($overall + $test_time)" )"
    done
    python -c "print 'time for %6s: %6.2f (avg %5.2f)' % ('${1}', $overall, $overall / $TEST_NUM)"
}

wc_l "${GREP_CMD[@]}"
wc_l "${PERL_CMD[@]}" 
wc_l "${AWK_CMD[@]}"
wc_l "${SED_CMD[@]}"
wc_l "${PYTHON_CMD[@]}" 

echo "tests number: $TEST_NUM"

rtime "${GREP_CMD[@]}"
rtime "${PERL_CMD[@]}" 
rtime "${AWK_CMD[@]}"
rtime "${SED_CMD[@]}"
rtime "${PYTHON_CMD[@]}"
+ /tmp/grep.py
#!/usr/bin/env python
#_*_ coding: utf-8 _*_

import sys, re

patt = re.compile(sys.argv[1])

with open(sys.argv[2], 'r') as f:
    for line in f:
        if patt.search(line):
            print line,
95% процентов проблем находятся между клавиатурой и стулом.

20

отсюда вопрос: valet, что у тебя за кривой awk такой?

[копирайты отрезаны]

alexander@goblin /tmp $ grep --version
grep (GNU grep) 2.14
alexander@goblin /tmp $ perl --version
This is perl 5, version 12, subversion 4 (v5.12.4) built for x86_64-linux
(with 12 registered patches, see perl -V for more detail)
alexander@goblin /tmp $ awk --version
GNU Awk 4.0.1
alexander@goblin /tmp $ sed --version
GNU sed версия 4.2.1
alexander@goblin /tmp $ python --version
Python 2.7.3
95% процентов проблем находятся между клавиатурой и стулом.

21 (28.02.2013 17:44:56 отредактировано -=Serj=-)

Fat-Zer, какая версия Python у тебя? И какая у valet?
3.2 медленнее 2.7, 3.3 чуть медленнее 2.7.

UPD. Ага, уже указал...

ЗЗЫ. Хм, у меня Python отрабатывает быстрее Go.

Да, у меня Ubuntu.
Нет, не глючит.

22

Кстати, возник такой вопрос. Как организовать многопоточное построчное чтение текстового файла?

Да, у меня Ubuntu.
Нет, не глючит.

23

-=Serj=- пишет:

Кстати, возник такой вопрос. Как организовать многопоточное построчное чтение текстового файла?

самое простое, что приходит на ум: mmap'ишь, первый поток читает с начала, второй — тычется в середину, находит конец строки и читает оттуда...

ИРЛ это даёт профит только если чтение с диска не требуется... так что отдельного потока чтения будет даже лучше в большинстве задач...

95% процентов проблем находятся между клавиатурой и стулом.

24 (28.02.2013 22:09:21 отредактировано valet)

valet@valet-comp:~/Documents/all$ time ./go-grep foo all > /dev/null 

real    0m10.354s
user    0m10.221s
sys    0m0.124s
valet@valet-comp:~/Documents/all$ time ./go-grep2 foo all > /dev/null 

real    0m6.957s
user    0m6.852s
sys    0m0.100s

Совсем как-то медленно, че-то этот GO не очень...

Fat-Zer, спасибо за результаты - интересно.

По поводу версий:

+ открыть спойлер
valet@valet-comp:~/Documents/all$ grep --version
GNU grep 2.6.3

Copyright (C) 2009 Free Software Foundation, Inc.
Лицензия GPLv3+: GNU GPL версии 3 или новее <http://gnu.org/licenses/gpl.html>
Это свободное ПО: вы можете продавать и распространять его.
Нет НИКАКИХ ГАРАНТИЙ до степени, разрешённой законом.

valet@valet-comp:~/Documents/all$ perl --version

This is perl, v5.10.1 (*) built for x86_64-linux-gnu-thread-multi
(with 59 registered patches, see perl -V for more detail)

Copyright 1987-2009, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

valet@valet-comp:~/Documents/all$ awk --version
GNU Awk 3.1.7
Copyright (C) 1989, 1991-2009 Free Software Foundation.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
valet@valet-comp:~/Documents/all$ sed --version
GNU sed версия 4.2.1
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,
to the extent permitted by law.

GNU sed home page: <http://www.gnu.org/software/sed/>.
General help using GNU software: <http://www.gnu.org/gethelp/>.
E-mail bug reports to: <bug-gnu-utils@gnu.org>.
Be sure to include the word ``sed'' somewhere in the ``Subject:'' field.
valet@valet-comp:~/Documents/all$ python --version
Python 2.6.6
valet@valet-comp:~/Documents/all$ 

И все таки - почему же grep явный лидер, что там код настолько кардинально лучше написан, что оно выигрывает. В чем секрет?

25

-=Serj=- пишет:

Кстати, возник такой вопрос. Как организовать многопоточное построчное чтение текстового файла?

xargs, не?

26

valet пишет:

По поводу версий:

awk за три года всё же улучшили, видимо...

valet пишет:

И все таки - почему же grep явный лидер, что там код настолько кардинально лучше написан, что оно выигрывает. В чем секрет?

просто он только и умеет, что искать строчки....

из явных отличий:
grep читает из файла по 32к блоками, а остальные(кроме питона) 4к-страницами....
скорей всего это из-за того, что sedperlawk читает через stdio'шные средства, а не системные... соответственно отсюда не меньше, чем 2-й оверхед при чтении..

за остальным - надо исходники курить... мне лень...

95% процентов проблем находятся между клавиатурой и стулом.

27

Fat-Zer пишет:

awk за три года всё же улучшили, видимо...

может, у меня искаропки, не менял. Но у тебя же тоже он скоростью не выделяется.

Fat-Zer пишет:

за остальным - надо исходники курить... мне лень...

Это понятно. Не знаю как кто, но я почему-то думал, что из всех этих grep самый медленный, просто его все юзают потому что он самый удобный, ошибался....

28 (01.03.2013 09:37:31 отредактировано -=Serj=-)

Fat-Zer пишет:

ИРЛ это даёт профит только если чтение с диска не требуется...

Просто для эксперимента оставил в варианте на Python и GO только построчное чтение файла, дык GO оказался в несколько раз медленнее...

Fat-Zer пишет:

так что отдельного потока чтения будет даже лучше в большинстве задач...

"Одного" ты хотел сказать?
Погуглив, выяснил, что GO быстрее работает со срезами байтов, чем со строками.

package main

import (
    "bufio"
    "fmt"
    "os"
    "regexp"
)

func main() {

    if len(os.Args) != 3 {
        fmt.Printf("Usage: %s <substring> <filename>\n", os.Args[0])
        return
    }
    substr := os.Args[1]
    fname := os.Args[2]

    f, err := os.Open(fname)
    if err != nil {
        return
    }
    defer f.Close()

    r := bufio.NewReader(f)
    patt, _ := regexp.Compile(substr)

    for {

        s, ok := r.ReadSlice('\n')
        if ok != nil {
            break
        }
        matches := patt.Match(s)
        if matches {
            fmt.Print(string(s))
        }

    }

}

Этот вариант в полтора раза быстрее... По скорости чтения слегка обошёл Python. Теперь затык в регулярке.

Да, у меня Ubuntu.
Нет, не глючит.

29

Про awk'и:
Есть несколько реализаций awk, которые принципиально отличаются по скорости.
Например на чтении 300МБ файла (1.5млн строк, источник) разница примерно следующая:
gawk - 0m1.871s
mawk - 0m0.469s

30

Civil пишет:

Есть несколько реализаций awk, которые принципиально отличаются по скорости.
Например на чтении 300МБ файла (1.5млн строк, источник) разница примерно следующая:

это то понятно... меня удивило, что между гнутыми 3-м и 4-м разница в 4 раза получается...

95% процентов проблем находятся между клавиатурой и стулом.