【问题标题】:Why is s/^\s+|\s+$//g; so much slower than two separate substitutions?为什么是s/^\s+|\s+$//g;比两个单独的替换慢得多?
【发布时间】:2011-01-19 19:26:31
【问题描述】:

Perl 常见问题解答条目 How do I strip blank space from the beginning/end of a string? 声明使用

s/^\s+|\s+$//g;

比分两步做要慢:

s/^\s+//;
s/\s+$//;

为什么这个组合语句明显比单独的语句慢(对于任何输入字符串)?

【问题讨论】:

标签: regex perl trim


【解决方案1】:

当使用“固定”或“锚定”子字符串而不是“浮动”子字符串时,Perl 正则表达式运行时运行得更快。当您可以将子字符串锁定到源字符串中的某个位置时,子字符串是固定的。 '^' 和 '$' 都提供锚定。但是,当您使用替代“|”时,编译器不会将选项识别为固定的,因此它使用不太优化的代码来扫描整个字符串。在过程结束时,两次查找固定字符串比查找一次浮动字符串要快得多。在相关的说明中,阅读 perl 的 regcomp.c 会让你失明。

更新: 这里有一些额外的细节。如果您已使用调试支持编译它,则可以使用“-Dr”标志运行 perl,它会转储正则表达式编译数据。这是你得到的:

~# debugperl -Dr -e 's/^\s+//g'
Compiling REx `^\s+'
size 4 Got 36 bytes for offset annotations.
first at 2
synthetic stclass "ANYOF[\11\12\14\15 {unicode_all}]".
   1: BOL(2)
   2: PLUS(4)
   3:   SPACE(0)
   4: END(0)
stclass "ANYOF[\11\12\14\15 {unicode_all}]" anchored(BOL) minlen 1

# debugperl -Dr -e 's/^\s+|\s+$//g'
Compiling REx `^\s+|\s+$'
size 9 Got 76 bytes for offset annotations.

   1: BRANCH(5)
   2:   BOL(3)
   3:   PLUS(9)
   4:     SPACE(0)
   5: BRANCH(9)
   6:   PLUS(8)
   7:     SPACE(0)
   8:   EOL(9)
   9: END(0)
minlen 1 

注意第一个转储中的“锚定”一词。

【讨论】:

  • 您实际上并不需要 perl 的调试版本。 -Dr 需要它才能工作,但没有它你可以 use re 'debug' 更漂亮。
【解决方案2】:

其他答案表明,完全锚定的正则表达式允许引擎优化搜索过程,只关注开头或结尾或字符串。通过使用不同长度的字符串比较两种方法的速度差异,您似乎可以看到这种优化的效果。随着字符串变长,“浮动”正则表达式(使用交替)受到的影响越来越大。

use strict;
use warnings;
use Benchmark qw(cmpthese);

my $ws = "   \t\t\n";

for my $sz (1, 10, 100, 1000){
    my $str = $ws . ('Z' x $sz) . $ws;
    cmpthese(-2, {
        "alt_$sz" => sub { $_ = $str; s/^\s+|\s+$//g },
        "sep_$sz" => sub { $_ = $str; s/^\s+//; s/\s+$// },
    });
}

           Rate alt_1 sep_1
alt_1  870578/s    --  -16%
sep_1 1032017/s   19%    --

            Rate alt_10 sep_10
alt_10  384391/s     --   -62%
sep_10 1010017/s   163%     --

            Rate alt_100 sep_100
alt_100  61179/s      --    -92%
sep_100 806840/s   1219%      --

             Rate alt_1000 sep_1000
alt_1000   6612/s       --     -97%
sep_1000 261102/s    3849%       --

【讨论】:

    【解决方案3】:

    由于这两种方法在逻辑上是等价的,因此它们的评估性能没有内在的差异。然而,在实践中,一些引擎将无法在更复杂的正则表达式中发现优化。

    在这种情况下,组合的正则表达式作为一个整体是未锚定的,因此它可能会在字符串中的任何点匹配,而 ^\s+ 锚定在开头,因此匹配很简单,而 \s+$锚定在末尾,并从末尾向后为每个字符提供单个字符类 - 一个经过良好优化的引擎将识别该事实并将反向匹配,这使得它与 ^\s+ 的反向匹配一样微不足道输入。

    【讨论】:

      【解决方案4】:

      如果情况确实如此,那是因为正则表达式引擎对单个正则表达式的优化比对组合的更好。

      “明显变慢”是什么意思?

      【讨论】:

      • @Anon:在 25 个字符的字符串上大约慢 5 倍
      • @Anon。请参阅常见问题解答perldoc.perl.org/…
      • 明显较慢意味着您可以注意到差异,在这种情况下您会看到巨大的速度损失。
      猜你喜欢
      • 2016-11-20
      • 1970-01-01
      • 1970-01-01
      • 2022-11-17
      • 2021-07-01
      • 2015-08-30
      • 2011-08-23
      • 1970-01-01
      • 2011-09-11
      相关资源
      最近更新 更多