gawk和....之间的性能差异是什么?

Ed *_*ton 5 c perl awk

这个问题已经在Meta上进行了讨论,我的回答给出了一个测试系统的链接来回答这个问题.


关于是否因为性能而使用gawk或mawk或C或其他语言的问题经常出现,所以让我们为一个简单而典型的awk程序创建一个规范的问题/答案.

其结果将是一个答案,它提供了对简单输入文件上执行正则表达式匹配和字段拆分的基本文本处理任务的不同工具的性能进行比较.如果工具X的速度是此任务的每个其他工具的两倍,则这是有用的信息.如果所有工具花费大约相同的时间,那么这也是有用的信息.

这将起作用的方式是,在接下来的几天内,许多人将提供"答案",这是要测试的程序,然后一个人(志愿者?)将在一个平台上测试所有这些(或者一些人将测试在他们的平台上的一些子集,所以我们可以比较)然后所有的结果将被收集到一个答案.

给定此脚本创建的1000万行输入文件:

$ awk 'BEGIN{for (i=1;i<=10000000;i++) print (i%5?"miss":"hit"),i,"  third\t \tfourth"}' > file

$ wc -l file
10000000 file

$ head -10 file
miss 1   third          fourth
miss 2   third          fourth
miss 3   third          fourth
miss 4   third          fourth
hit 5   third           fourth
miss 6   third          fourth
miss 7   third          fourth
miss 8   third          fourth
miss 9   third          fourth
hit 10   third          fourth
Run Code Online (Sandbox Code Playgroud)

并给出这个awk脚本,打印每行第4个第1个和第3个字段,以"hit"开头,后跟偶数:

$ cat tst.awk
/hit [[:digit:]]*0 / { print $4, $1, $3 }
Run Code Online (Sandbox Code Playgroud)

以下是预期输出的前5行:

$ awk -f tst.awk file | head -5
fourth hit third
fourth hit third
fourth hit third
fourth hit third
fourth hit third
Run Code Online (Sandbox Code Playgroud)

这是通过管道传输到第二个awk脚本来验证上面的主脚本实际上是否按预期运行时的结果:

$ awk -f tst.awk file |
awk '!seen[$0]++{unq++;r=$0} END{print ((unq==1) && (seen[r]==1000000) && (r=="fourth hit third")) ? "PASS" : "FAIL"}'
PASS
Run Code Online (Sandbox Code Playgroud)

以下是在cygwin64上以bash 4.3.33运行的第3次执行 gawk 4.1.1 的时序结果:

$ time awk -f tst.awk file > /dev/null
real    0m4.711s
user    0m4.555s
sys     0m0.108s
Run Code Online (Sandbox Code Playgroud)

请注意,上面是删除缓存差异的第3次执行.

任何人都可以提供等效的C,perl,python,无论代码如何:

$ cat tst.awk
/hit [[:digit:]]*0 / { print $4, $1, $3 }
Run Code Online (Sandbox Code Playgroud)

即在一条线上找到THAT REGEXP(我们不是在寻找一些适用于regexp的其他解决方案),在每个连续的空白系列中分割线并打印第4个,然后是第1个,然后是第3个字段,由一个空白的字母?

如果是这样,我们可以在一个平台上测试它们以查看/记录性能差异.


到目前为止的代码贡献:

AWK(可以针对gawk等进行测试,但是mawk,nawk和其他人可能需要[0-9]而不是[:digit:])

awk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file
Run Code Online (Sandbox Code Playgroud)

PHP

php -R 'if(preg_match("/hit \d*0 /", $argn)){$f=preg_split("/\s+/", $argn); echo $f[3]." ".$f[0]." ".$f[2];}' < file
Run Code Online (Sandbox Code Playgroud)

贝壳

egrep 'hit [[:digit:]]*0 ' file | awk '{print $4, $1, $3}'
grep --mmap -E "^hit [[:digit:]]*0 " file | awk '{print $4, $1, $3 }'
Run Code Online (Sandbox Code Playgroud)

红宝石

$ cat tst.rb
File.open("file").readlines.each do |line|
  line.gsub(/(hit)\s[0-9]*0\s+(.*?)\s+(.*)/) { puts "#{$3} #{$1} #{$2}" }
end
$ ruby tst.rb
Run Code Online (Sandbox Code Playgroud)

Perl的

$ cat tst.pl
#!/usr/bin/perl -nl
# A solution much like the Ruby one but with atomic grouping
print "$4 $1 $3" if /^(hit)(?>\s+)(\d*0)(?>\s+)((?>[^\s]+))(?>\s+)(?>([^\s]+))$/
$ perl tst.pl file
Run Code Online (Sandbox Code Playgroud)

蟒蛇

none yet
Run Code Online (Sandbox Code Playgroud)

C

none yet
Run Code Online (Sandbox Code Playgroud)

hek*_*mgl 2

这是 PHP 中的等价内容:

$ time php -R 'if(preg_match("/hit \d*0 /", $argn)){$f=preg_split("/\s+/", $argn); echo $f[3]." ".$f[0]." ".$f[2];}' < file > /dev/null
real    2m42.407s
user    2m41.934s
sys 0m0.355s
Run Code Online (Sandbox Code Playgroud)

与您的相比awk

$ time awk -f tst.awk file > /dev/null
real    0m3.271s
user    0m3.165s
sys 0m0.104s
Run Code Online (Sandbox Code Playgroud)

我在 PHP 中尝试了一种不同的方法,我手动迭代文件,这使得事情变得更快,但我仍然没有留下深刻的印象:

tst.php

<?php

$fd=fopen('file', 'r');
while($line = fgets($fd)){
    if(preg_match("/hit \d*0 /", $line)){
        $f=preg_split("/\s+/", $line);
        echo $f[3]." ".$f[0]." ".$f[2]."\n";
    }
}
fclose($fd);
Run Code Online (Sandbox Code Playgroud)

结果:

$ time php  tst.php > /dev/null 
real    0m27.354s
user    0m27.042s
sys 0m0.296s
Run Code Online (Sandbox Code Playgroud)