Regex 为什么再添加一个选项会使我的正则表达式速度慢600倍以上?

Regex 为什么再添加一个选项会使我的正则表达式速度慢600倍以上?,regex,performance,perl,Regex,Performance,Perl,在测试一个简单的Perl脚本时,我注意到它应该过滤掉以某些前缀开头的文件名 基本上,我正在构建一个正则表达式,如下所示: my$regex=join“|”,map quotemeta,@前缀; $regex=qr/^($regex)/;#锚定正则表达式并预编译它 这里,在我最初测试的场景中,@前缀由32个字符的十六进制字符串组成。我发现,在6552个前缀之前,一切都运行得很好,很顺利——但当我尝试6553时,的执行时间就增加了25倍多(从1.8秒增加到51秒) 我对它进行了研究,并构建了以下基

在测试一个简单的Perl脚本时,我注意到它应该过滤掉以某些前缀开头的文件名

基本上,我正在构建一个正则表达式,如下所示:

my$regex=join“|”,map quotemeta,@前缀;
$regex=qr/^($regex)/;#锚定正则表达式并预编译它
这里,在我最初测试的场景中,
@前缀
由32个字符的十六进制字符串组成。我发现,在6552个前缀之前,一切都运行得很好,很顺利——但当我尝试6553时,的执行时间就增加了25倍多(从1.8秒增加到51秒)

我对它进行了研究,并构建了以下基准。我最初使用了32个字符的十六进制字符串,就像在我的原始程序中一样,但发现如果我将字符串的长度减少到8个字符,则阈值会更高,达到16383,事实上,尽管减速系数变得更高了:具有16383个备选方案的regexp几乎是只有16382个备选方案的regexp的650倍

#/usr/bin/perl
严格使用;
使用警告;
使用基准qw(这些cmpthese的时间);
我的$count=shift | | 10;
我们的@items=地图解包(“H8”,包装“V”,美元),0..9999;
我们的$nA=16382;我们的$reA=加入“|”,@items[1..$nA];
我们的新台币=16383美元;我们的$reB=加入“|”,@items[1..$nB];
美元=qr/^($)/对于$reA,$reB;#锚定并编译正则表达式
my$results=timethis$count{
$nA=>q{our(@items,$nA,$reA);$nA==grep/$reA/,@items或die;},
$nB=>q{our(@items,$nB,$reB);$nB==grep/$reB/,@items或die;},
} );
cmp这些($结果);
以下是使用Perl(v5.18.2)在我的笔记本电脑上运行此基准测试的结果:

基准测试:为1638216383的10次迭代计时。。。
16382:2个时钟秒(1.79USR+0.00SYS=1.79CPU)@5.59/s(n=10)
16383:1163壁时钟秒(1157.28 usr+2.70 sys=1159.98 CPU)@0.01/s(n=10)
s/iter 16382
16383    116     --  -100%
16382  0.179 64703%     --
注意64703%的速度差

我最初的预感是基于6553&approw;216/10,这可能是Perl正则表达式引擎中的某种任意限制,或者可能存在某种10字节结构的数组,限制为64 kB,或者其他什么。但备选方案的临界数量取决于它们的长度,这一事实使事情变得更加混乱

(另一方面,显然也不仅仅是正则表达式的长度;具有6553个32字节替代项的原始正则表达式的长度为2+6553×33=216251字节,而具有16383个8字节替代项的正则表达式的长度仅为2+16383×9=147450字节。)


是什么导致正则表达式匹配时间出现这种奇怪的跳跃,为什么会发生在那个特定点?

很长一段时间以来,在正则表达式的初始编译生成longjmp而不是jmp(我认为)指令的地方,perl的TRIE优化并没有得到应用(这取决于替换的数量和所涉及字符串的总长度,以及正则表达式中的其他内容(更早?)

请参见以下内容之间的区别:

perl -we'use re "debug"; qr/@{[join"|","a".."afhd"]}/'


您可以将您的替换分解为更小的块,并分别预编译它们,例如,
(??{$rxa})|(??{$rxb})|(??{$rxc})

另一个可能的因素(即可能不是整个图片)正则表达式数据结构是否变得足够大,以至于它们不再适合您的一级/二级/三级缓存…虽然我觉得这个问题非常值得回答,但我也觉得出于实际目的,您应该使用映射查找而不是16k项正则表达式。@Mr.Llama:当然。原始脚本用于大约500个备选方案,regex解决方案对其运行良好。我只是好奇它将如何扩展。
perl -we'use re "debug"; qr/@{[join"|","a".."afhe"]}/'