此问题已经过讨论here on Meta,我的answer会提供指向测试系统的链接以解答此问题。
由于性能问题,经常会出现关于是否使用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
并且给出了这个awk脚本,它打印了以“hit”开头的每一行的第4个然后第1个和第3个字段,然后是偶数:
$ cat tst.awk
/hit [[:digit:]]*0 / { print $4, $1, $3 }
以下是预期输出的前5行:
$ awk -f tst.awk file | head -5
fourth hit third
fourth hit third
fourth hit third
fourth hit third
fourth hit third
这是通过管道传输到第二个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
以下是在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
注意以上是删除缓存差异的第3次执行。
任何人都可以提供等效的C,perl,python,无论代码如何:
$ cat tst.awk
/hit [[:digit:]]*0 / { print $4, $1, $3 }
即。在一条线上找到THAT REGEXP(我们不是在寻找一些适用于正则表达式的其他解决方案),在每个连续的空白系列中分割线并打印第4个,然后是第1个,然后是第3个由a分隔的字段单个空白字符?
如果是这样,我们可以在一个平台上测试它们以查看/记录性能差异。
到目前为止的代码:
AWK(可以针对gawk等进行测试,但是mawk,nawk和其他人可能需要[0-9]而不是[:digit:])
awk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file
PHP
php -R 'if(preg_match("/hit \d*0 /", $argn)){$f=preg_split("/\s+/", $argn); echo $f[3]." ".$f[0]." ".$f[2];}' < file
壳
egrep 'hit [[:digit:]]*0 ' file | awk '{print $4, $1, $3}'
grep --mmap -E "^hit [[:digit:]]*0 " file | awk '{print $4, $1, $3 }'
红宝石
$ 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
的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
的Python
none yet
C
none yet
答案 0 :(得分:4)
在awk之前应用egrep可以提高速度:
paul@home ~ % wc -l file
10000000 file
paul@home ~ % for i in {1..5}; do time egrep 'hit [[:digit:]]*0 ' file | awk '{print $4, $1, $3}' | wc -l ; done
1000000
egrep --color=auto 'hit [[:digit:]]*0 ' file 0.63s user 0.02s system 85% cpu 0.759 total
awk '{print $4, $1, $3}' 0.70s user 0.01s system 93% cpu 0.760 total
wc -l 0.00s user 0.02s system 2% cpu 0.760 total
1000000
egrep --color=auto 'hit [[:digit:]]*0 ' file 0.65s user 0.01s system 85% cpu 0.770 total
awk '{print $4, $1, $3}' 0.71s user 0.01s system 93% cpu 0.771 total
wc -l 0.00s user 0.02s system 2% cpu 0.771 total
1000000
egrep --color=auto 'hit [[:digit:]]*0 ' file 0.64s user 0.02s system 82% cpu 0.806 total
awk '{print $4, $1, $3}' 0.73s user 0.01s system 91% cpu 0.807 total
wc -l 0.02s user 0.00s system 2% cpu 0.807 total
1000000
egrep --color=auto 'hit [[:digit:]]*0 ' file 0.63s user 0.02s system 86% cpu 0.745 total
awk '{print $4, $1, $3}' 0.69s user 0.01s system 92% cpu 0.746 total
wc -l 0.00s user 0.02s system 2% cpu 0.746 total
1000000
egrep --color=auto 'hit [[:digit:]]*0 ' file 0.62s user 0.02s system 88% cpu 0.727 total
awk '{print $4, $1, $3}' 0.67s user 0.01s system 93% cpu 0.728 total
wc -l 0.00s user 0.02s system 2% cpu 0.728 total
与
paul@home ~ % for i in {1..5}; do time gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null; done
gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 2.46s user 0.04s system 97% cpu 2.548 total
gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 2.43s user 0.03s system 98% cpu 2.508 total
gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 2.40s user 0.04s system 98% cpu 2.489 total
gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 2.38s user 0.04s system 98% cpu 2.463 total
gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 2.39s user 0.03s system 98% cpu 2.465 total
&#39; NAWK&#39;甚至更慢!
paul@home ~ % for i in {1..5}; do time nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null; done
nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 6.05s user 0.06s system 92% cpu 6.606 total
nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 6.11s user 0.05s system 96% cpu 6.401 total
nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 5.78s user 0.04s system 97% cpu 5.975 total
nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 5.71s user 0.04s system 98% cpu 5.857 total
nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null 6.34s user 0.05s system 93% cpu 6.855 total
答案 1 :(得分:3)
在OSX Yosemite上
time bash -c 'grep --mmap -E "^hit [[:digit:]]*0 " file | awk '\''{print $4, $1, $3 }'\''' >/dev/null
real 0m5.741s
user 0m6.668s
sys 0m0.112s
答案 2 :(得分: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
与您的awk
相比:
$ time awk -f tst.awk file > /dev/null
real 0m3.271s
user 0m3.165s
sys 0m0.104s
我在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);
结果:
$ time php tst.php > /dev/null
real 0m27.354s
user 0m27.042s
sys 0m0.296s
答案 3 :(得分:2)
第一个想法
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'agendaWeek'
},
defaultDate: '2015-04-20',
editable: false,
eventLimit: true,
firstDay: 1,
defaultView: 'agendaWeek',
timeFormat: 'HH:mm',
lang: 'de',
columnFormat: 'dddd D.M.',
allDaySlot: false,
slotDuration: '00:30:00', // default is 30 min
minTime: '12:00:00',
// maxTime: '23:00:00',
contentHeight: 600,
events: [
{
title: '',
start: '2014-09-01T18:00:00',
end: '2014-09-01T20:00:00',
dow: [1], // repeat same weekday
rendering: 'background',
color: '#6BA5C2'
},
{
title: '',
start: '2014-09-02T20:00:00',
end: '2014-09-02T22:00:00',
dow: [2], // repeat same weekday
rendering: 'background',
color: '#6BA5C2'
},
{
title: 'Event',
start: '2015-04-24 13:00:00',
end: '2015-04-24 14:00:00',
color: '#F77'
},
],
// * no callback
viewDisplay: function (element) {
alert(element);
},
// adding an event
selectable: true,
selectHelper: true,
select: function(start, end) {
var title = prompt('Deine E-Mail:');
var eventData;
if(title) {
eventData = {
title: title,
start: start,
end: end,
color: '#00AA00',
editable: true,
eventDurationEditable: true,
// * no callback
eventResizeStop: function(event, jsEvent, ui, view) {
alert('end');
},
// * no callback
eventClick: function(calEvent, jsEvent, view) {
alert('clicked');
},
// * no callback
eventDrop: function(event, delta, revertFunc, jsEvent, ui, view) {
alert('dropped');
},
// * no callback
eventResize: function(event, delta, revertFunc, jsEvent, ui, view) {
alert(event.title + ' end is now ' + event.end.format());
},
};
$('#calendar').fullCalendar('renderEvent', eventData, true); // stick = true
$('#calendar').fullCalendar('refetchEvents');
// not helping either
$('#calendar').fullCalendar('rerenderEvents');
}
$('#calendar').fullCalendar('unselect');
},
});
第二个想法
File.open("file").readlines.each do |line|
line.gsub(/(hit)\s[0-9]*0\s+(.*?)\s+(.*)/) { puts "#{$3} #{$1} #{$2}" }
end
尝试获得能够比较答案的内容我最终创建了一个github repo here。每次推送到此repo都会触发travis-ci上的构建,该构建将构成一个markdown文件,该文件依次推送到gh-pages分支,以更新web page以及构建结果的视图。
任何希望参与的人都可以分享github仓库,添加测试并执行pull request,如果它不会破坏其他测试,我会尽快合并。
答案 4 :(得分:1)
mawk
略快于gawk
。
$ time bash -c 'mawk '\''/hit [[:digit:]]*0 / { print $4, $1, $3 }'\'' file | wc -l'
0
real 0m1.160s
user 0m0.484s
sys 0m0.052s
$ time bash -c 'gawk '\''/hit [[:digit:]]*0 / { print $4, $1, $3 }'\'' file | wc -l'
100000
real 0m1.648s
user 0m0.996s
sys 0m0.060s
(我的输入文件中只有1,000,000行。许多显示的最佳结果,尽管它们非常一致。)