Perl v5.28.1
基准
:use common::sense;
use Benchmark qw(:all);
my $UPPER = 10_000_000;
my $str = 'foo bar baz';
cmpthese(10, {
'empty for-loop' => sub {
for my $i (1..$UPPER) {}
},
'regex match' => sub {
for my $i (1..$UPPER) {
$str =~ /foo/;
}
},
'regex match (single compile)' => sub {
my $re = qr/foo/;
for my $i (1..$UPPER) {
$str =~ $re;
}
},
'regex match (anchor)' => sub {
for my $i (1..$UPPER) {
$str =~ /^foo/;
}
},
'regex match (anchor) (single compile)' => sub {
my $re = qr/^foo/;
for my $i (1..$UPPER) {
$str =~ $re;
}
},
});
结果 :
s/iter regex match (anchor) (single compile) regex match (single compile) regex match (anchor) regex match empty for-loop
regex match (anchor) (single compile) 3.83 -- -21% -60% -84% -97%
regex match (single compile) 3.04 26% -- -50% -80% -96%
regex match (anchor) 1.53 151% 99% -- -61% -92%
regex match 0.601 537% 405% 154% -- -81%
empty for-loop 0.117 3170% 2496% 1205% 414% --
因为 foo 碰巧发生在字符串的开头,我希望在正则表达式中添加一个显式锚点 (^) 什么都不做......不是性能减半!
同样,我读到了一些关于Perl足够聪明的东西,即使包含在循环中,也不会重新编译带有固定字符串的表达式。
但是,为什么尝试手动/显式地将表达式"预编译"为变量$re会导致这样的性能下降?!
我将搜索子字符串"foo"更改为"asdf"(这在$str中不会出现),锚定确实让引擎更快地退出搜索。 但是将表达式分配给变量仍然是一个巨大的性能影响 - 比我预期的要大得多!:
Rate regex match (single compile) regex match (anchor) (single compile) regex match regex match (anchor) empty for-loop
regex match (single compile) 0.401/s -- -10% -79% -83% -96%
regex match (anchor) (single compile) 0.447/s 11% -- -76% -81% -95%
regex match 1.88/s 369% 321% -- -19% -79%
regex match (anchor) 2.33/s 481% 421% 24% -- -75%
empty for-loop 9.17/s 2185% 1951% 387% 294% --
所以总结2个问题:
- 为什么字符串开头锚点的性能应该减半? - 为什么将表达式 (qr//) 编译为变量比在内联使用相同的表达式慢 80%?
添加锚点会阻止发生特定的正则表达式优化。此问题已在 5.30.0 中修复。
使用 qr//对象目前会产生轻微的惩罚,因为内部必须复制正则表达式结构的一部分(与每个正则表达式对象都有自己的一组捕获索引的事实有关)。还没有人想到一个很好的解决方案。