如何对Perl代码进行速度基准测试

使用Benchmark模块,对Perl代码进行速度基准测试非常简单。本文将讨论基准测试的一般方法和如何使用Benchmark模块。

什么是基准测试?

在编程中,基准测试是对某些代码在特定时间点的性能进行测量。代码性能的任何方面都可以进行基准测试:速度、内存使用和IO频率是一些常见的指标。

基准测试的限制

在基准测试中需要注意的一点是,它们受环境的影响:操作系统、硬件、软件和当前机器状态都可能影响基准测试。对于Perl代码来说,当前的Perl版本和Perl安装时使用的编译选项可以对代码性能产生重大影响。例如,如果Perl是用iThreads启用编译的,这将增加所有Perl程序的开销。因此,只有在基准测试环境相同的情况下,基准测试比较才有意义。

Perl的Benchmark模块

Benchmark 已包含在Perl核心中,因此如果您已安装Perl,则应该已经安装了Benchmark。如果您在使用类UNIX的系统上,则可以考虑安装Benchmark::Forking,因为它可以提高基准测试的准确性。Benchmark::ForkingBenchmark 的一个即插即用替代品,以下的所有代码示例都将与这两个模块一起工作。

计时Perl代码

当比较代码性能时,基准测试最为有趣——因此我们将关注执行此操作的方法。Benchmark提供了一个timethese子程序,它连续执行一组Perl代码一定数量的CPU秒,然后打印出结果。让我们比较两个常用Perl操作的执行速度:使用shift内置函数进行数组赋值和直接使用等于进行数组赋值。

use strict;
use warnings;
use Benchmark qw/cmpthese timethese/;

timethese(-10, {
        shiftAssign => sub {    my @alphabet = ('A'..'Z');
                                for (my $i = 0; $i < 26; $i++){
                                    my $letter = shift @alphabet;
                                }
                           },
        equalsAssign => sub {   my @alphabet = ('A'..'Z');
                                for (my $i = 0; $i < 26; $i++){
                                    my $letter = $alphabet[$i];
                                }
                            },
});

运行上述代码产生了以下结果

Benchmark: running equalsAssign, shiftAssign for at least 10 CPU seconds...
equalsAssign: 10 wallclock secs (10.32 usr +  0.00 sys = 10.32 CPU) @ 150112.98/s (n=1549166)
shiftAssign: 11 wallclock secs (10.43 usr +  0.00 sys = 10.43 CPU) @ 148529.82/s (n=1549166)

要关注的关键指标是每CPU秒的速率。这表明shiftAssign代码块以148,529.82/s的速度执行,而equalsAssign块执行得稍微快一点,以150,112.98/s的速度执行。请注意,每个块的测试运行了不同长的时间——因此输出中的其他数字不可直接比较。

比较Perl代码

Benchmark模块提供的cmpthese子程序接受与上面所示的timethese相同的参数,但它会打印出有用的比较表格,显示哪个代码块更快,以及其百分比。方便的是,它还包括每CPU秒的速率。

use strict;
use warnings;
use Benchmark qw/cmpthese timethese/;

cmpthese(-10, {
        shiftAssign => sub {    my @alphabet = ('A'..'Z');
                                for (my $i = 0; $i < 26; $i++){
                                    my $letter = shift @alphabet;
                                }
                           },
        equalsAssign => sub {   my @alphabet = ('A'..'Z');
                                for (my $i = 0; $i < 26; $i++){
                                    my $letter = $alphabet[$i];
                                }
                            },
});

执行此代码返回以下结果

                 Rate  shiftAssign equalsAssign
shiftAssign  142529/s           --          -4%
equalsAssign 148159/s           4%           --

上述结果按从慢到快的顺序排列(如速率/s所示)。这个基准测试表明,equalsAssign代码块比shiftAssign代码块快4%。

其他技巧

  • 尽量使用最少的代码来满足所需的行为——这将提高基准测试的准确性。
  • timethesecmpthese指定负数作为CPU秒计数。这指定了要运行的最小CPU秒数。一些来源推荐至少-5秒以避免不准确的基准测试。
  • 对结果进行合理性检查:如果您不确定,请尝试比较两个代码块,其中一个显然比另一个慢,以检查Benchmark是否返回合理的结果。
  • 比较代码示例而不是单独计时:代码块的实际执行时间通常并不重要;知道哪组代码比另一组快是有用的,因为这通常是一个可重复发生的情况。
  • 如果您需要更深入的基准测试,请考虑使用Devel::NYTProf

来源

本文特别借鉴了以下几方面的信息:brian d foy的Benchmarking Perl讲义和David Golden的Adventures in Benchmarking文章。


本文最初发布在PerlTricks.com

标签

David Farrell

David是一位职业程序员,他经常在Twitter博客上分享关于代码和编程艺术的文章。

浏览他们的文章

反馈

这篇文章有什么问题吗?请通过在GitHub上打开一个issue或pull request来帮助我们。