Perl中三元运算符的优化
Optimization of the ternary operator in Perl
我有这个循环:
for my $line (split /\n/, $content) {
($line !~ /^\-{2,}$/) ? ( $return .= "$line\n" )
: ( $return .= "\N{ZERO WIDTH SPACE}$line\n" );
}
大部分行与正则表达式不匹配(即:大多数情况下条件为真)。
我首先使用 =~
运算符编写了条件(交换了两个条件指令),但这是 second 指令最常执行的的时代。
换句话说......当你有一个测试,你知道它会在 99% 的情况下选择一个分支,它会改变一些东西(性能)来写它首先用那个分支吗?
When you have a test which you know that it will choose one branch in 99% of the cases, does it change something (performance) to write it with that branch first?
在简单的 if/else 情况下(即三元运算符),答案是 否。分支的顺序无关紧要,条件每次都会运行并选择要走的分支。
在 if/elsif/else 的情况下,这很重要,因为有多个条件 运行。将最常见的情况放在第一位会使事情变得更快。
如果 if/else 选择对 reader 最有意义的顺序,这通常意味着避免否定。 $line =~ /^\-{2,}$/
比 $line !~ /^\-{2,}$/
更易读。 $line =~ /^-{2,}$/
更好(不需要在正则表达式中转义 -
)。
至少应该没关系。对于任何像 Perl 这样复杂的东西,最好对这些东西进行基准测试。想出一些可以充分锻炼 CPU 的东西,以免在正常的基准测试抖动中丢失,这有点麻烦。在得出结论之前,请务必运行多次迭代并进行大量迭代。
use strict;
use warnings;
use v5.10;
use Benchmark qw(cmpthese);
my $Iterations = shift;
my $Threshhold = 100_000;
# I've picked something that isn't constant to avoid constant folding
sub a_then_b {
my $num = shift;
return $num > $Threshhold ? sqrt($num) + sqrt($num) ** 2
: $num + $num;
}
sub b_then_a {
my $num = shift;
return $num <= $Threshhold ? $num + $num
: sqrt($num) + sqrt($num) ** 2;
}
say "First one side";
cmpthese $Iterations, {
a_then_b => sub { a_then_b($Threshhold - 1) },
b_then_a => sub { b_then_a($Threshhold - 1) }
};
say "Then the other";
cmpthese $Iterations, {
a_then_b => sub { a_then_b($Threshhold + 1) },
b_then_a => sub { b_then_a($Threshhold + 1) }
};
最后一点,要充分利用三元组,赋值应该放在左侧。三元returns其分支的结果。
$return .= $line =~ /^-{2,}$/ ? "\N{ZERO WIDTH SPACE}$line\n"
: "$line\n";
您可能会想到,在 if ... elsif ... elsif ... else
链中,如果测试按概率降序编写,效率最高。这最大限度地减少了预期的测试次数,并且应该会产生更快的代码。但在您的情况下,您只有一个测试,因此它已经排序,并且反转该测试的逻辑是无关紧要的。
在任何情况下,您都担心细节过于精细而无法产生任何显着差异。您应该始终将所有代码编写为清晰和可读尽可能
只有在您完成代码的编写和调试之后,您才应该考虑性能。大多数情况下,您的 运行 时间会足够快,而且由于您是为了可读性而编写的,因此它也将是高度可维护的
如果您的代码需要优化,那么您应该首先分析它以找到瓶颈。我发现 never 完全没有任何用处。无论您使用哪种语言,我都希望分支和无分支之间的区别微不足道
我希望看到更多地道的 Perl。除了像我在上面的评论中写的那样逐行阅读你的文件,我会使用默认变量 $_
并添加你的 零宽度 space 独立于该行的其余部分
for ( split /\n/, $content ) {
$return .= "\N{ZERO WIDTH SPACE}" if /^-{2,}$/;
$return .= "$_\n";
}
我有这个循环:
for my $line (split /\n/, $content) {
($line !~ /^\-{2,}$/) ? ( $return .= "$line\n" )
: ( $return .= "\N{ZERO WIDTH SPACE}$line\n" );
}
大部分行与正则表达式不匹配(即:大多数情况下条件为真)。
我首先使用 =~
运算符编写了条件(交换了两个条件指令),但这是 second 指令最常执行的的时代。
换句话说......当你有一个测试,你知道它会在 99% 的情况下选择一个分支,它会改变一些东西(性能)来写它首先用那个分支吗?
When you have a test which you know that it will choose one branch in 99% of the cases, does it change something (performance) to write it with that branch first?
在简单的 if/else 情况下(即三元运算符),答案是 否。分支的顺序无关紧要,条件每次都会运行并选择要走的分支。
在 if/elsif/else 的情况下,这很重要,因为有多个条件 运行。将最常见的情况放在第一位会使事情变得更快。
如果 if/else 选择对 reader 最有意义的顺序,这通常意味着避免否定。 $line =~ /^\-{2,}$/
比 $line !~ /^\-{2,}$/
更易读。 $line =~ /^-{2,}$/
更好(不需要在正则表达式中转义 -
)。
至少应该没关系。对于任何像 Perl 这样复杂的东西,最好对这些东西进行基准测试。想出一些可以充分锻炼 CPU 的东西,以免在正常的基准测试抖动中丢失,这有点麻烦。在得出结论之前,请务必运行多次迭代并进行大量迭代。
use strict;
use warnings;
use v5.10;
use Benchmark qw(cmpthese);
my $Iterations = shift;
my $Threshhold = 100_000;
# I've picked something that isn't constant to avoid constant folding
sub a_then_b {
my $num = shift;
return $num > $Threshhold ? sqrt($num) + sqrt($num) ** 2
: $num + $num;
}
sub b_then_a {
my $num = shift;
return $num <= $Threshhold ? $num + $num
: sqrt($num) + sqrt($num) ** 2;
}
say "First one side";
cmpthese $Iterations, {
a_then_b => sub { a_then_b($Threshhold - 1) },
b_then_a => sub { b_then_a($Threshhold - 1) }
};
say "Then the other";
cmpthese $Iterations, {
a_then_b => sub { a_then_b($Threshhold + 1) },
b_then_a => sub { b_then_a($Threshhold + 1) }
};
最后一点,要充分利用三元组,赋值应该放在左侧。三元returns其分支的结果。
$return .= $line =~ /^-{2,}$/ ? "\N{ZERO WIDTH SPACE}$line\n"
: "$line\n";
您可能会想到,在 if ... elsif ... elsif ... else
链中,如果测试按概率降序编写,效率最高。这最大限度地减少了预期的测试次数,并且应该会产生更快的代码。但在您的情况下,您只有一个测试,因此它已经排序,并且反转该测试的逻辑是无关紧要的。
在任何情况下,您都担心细节过于精细而无法产生任何显着差异。您应该始终将所有代码编写为清晰和可读尽可能
只有在您完成代码的编写和调试之后,您才应该考虑性能。大多数情况下,您的 运行 时间会足够快,而且由于您是为了可读性而编写的,因此它也将是高度可维护的
如果您的代码需要优化,那么您应该首先分析它以找到瓶颈。我发现 never 完全没有任何用处。无论您使用哪种语言,我都希望分支和无分支之间的区别微不足道
我希望看到更多地道的 Perl。除了像我在上面的评论中写的那样逐行阅读你的文件,我会使用默认变量 $_
并添加你的 零宽度 space 独立于该行的其余部分
for ( split /\n/, $content ) {
$return .= "\N{ZERO WIDTH SPACE}" if /^-{2,}$/;
$return .= "$_\n";
}