基准测试子程序签名

子程序签名将在几天内发布到Perl核心。但它们的性能与传统方法(如直接变量赋值和Method::Signatures模块)相比如何?我对所有三个进行了基准测试,结果很有趣。

背景

介绍了新子程序签名功能,当它们首次出现在Perl开发版本5.19.9中时。对于这些基准测试,我使用了最新的Perl开发版本(5.19.11)。

方法

所有基准测试都来自以下代码的变体

use strict;
use warnings;
use Benchmark::Forking 'cmpthese';
use feature 'signatures';
no warnings 'experimental::signatures';
use Method::Signatures;

sub native_assignment { 
    die "Too few arguments for subroutine $!" unless @_ == 1; 
    my ($var) = @_;
}

sub native_signature ($var) {}

func method_signature ($var) {}

cmpthese(-5, {
    native_assignment=> sub { native_assignment(1)},
    native_signature => sub { native_signature(1) },
    method_signature => sub { method_signature(1) },
});

代码首先导入必要的库。行“no warnings ‘experimental::signatures’阻止Perl警告使用子程序签名。然后,代码声明了我们要测试的三个子程序:一个是正常的变量赋值,一个是本地子程序签名,另一个是Method::Signatures(“func”)。

由于基准测试模块按字母顺序执行测试,因此每个基准测试都运行了三次,每次测试重命名一次以更改测试顺序(每个测试在三个基准测试中都是第一次、第二次和第三次运行)。

结果

运行此基准测试返回以下结果

native_signature method_signature native_assignment
native_signature -- -10% -27%
method_signature 12% -- -19%
native_assignment 38% 23% --

结果显示本地子程序签名比Method::Signatures函数慢约12%,比本地赋值子程序慢38%。这是干净的语法的代价吗?实际上,这并不是全部的故事。

更改变量的数量

更改子程序中分配的变量数量会影响三种子程序类型的相对性能吗?我重新运行了基准测试,但这次增加要分配的变量数量,并绘制了结果

Comparison of signatures speed with increasing number of variables

结果显示,增加变量的数量提高了本地子程序签名相对于Method::Signatures的相对速度。对于两个变量赋值,它们的速度大致相同。对于三个或更多变量,本地子程序签名表现更好,快18%。当我与Ricardo Signes讨论这些结果时,他证实了本地子程序签名代码已经针对多个变量赋值进行了优化,这与上述结果显示相关。

更快的子程序签名

可以认为本地子程序签名已经足够快,并且与变量赋值和Method::Signatures相比提供了几个优点。然而,Ricardo确实与我分享了一个使子程序签名运行得更快的技巧,我忍不住要分享。

将匿名贪婪参数(“@”)添加到子程序签名中,移除了可以传递给子程序的最大参数数量的限制。让我们在我们的基准测试代码中将贪婪参数添加到子程序签名中。我还更新了代码以接受两个参数 - 在以前Method::Signatures和子程序签名表现相似的性能级别

use strict;
use warnings;
use Benchmark::Forking 'cmpthese';
use feature 'signatures';
no warnings 'experimental::signatures';
use Method::Signatures;

sub native_assignment { 
    die "Too few arguments for subroutine $!" unless @_ == 2; 
    my ($var1, $var2) = @_;
}

sub native_signature ($var1, $var2, @) {}

func method_signature ($var1, $var2) {}

cmpthese(-5, {
    native_assignment=> sub { native_assignment(1, 2)},
    native_signature => sub { native_signature(1, 2) },
    method_signature => sub { method_signature(1, 2) },
});

以下是结果

method_signature native_signature native_assignment
method_signature -- -23% -37%
native_signature 30% -- -18%
native_assignment 60% 23% --

通过添加贪婪参数,本地子程序签名的性能提高了30%!这是因为子程序不再需要运行一个变量计数检查,以检查签名接受的变量的上限。您可以选择是否为了性能提升而移除此检查 - 我想不出一个值得这样做的情况,但谁又知道呢。

结论

子例程变量分配是一种相对低成本的运算,不太可能成为代码运行时间的瓶颈。然而,速度基准测试表明,通过切换到子例程签名不太可能导致性能下降,在某些情况下甚至可以提升运行速度。所以请放心使用它们!

谢谢

感谢Perl大牛Ricardo Signes为我们提供了关于子例程签名实现和slury参数优化的详细信息。

喜欢这篇文章吗?请帮助我们,在微博上分享吧!

*编辑:文章代码和基准测试于2014/05/12进行了针对单个变量分配的修正*


本文最初发布在PerlTricks.com

标签

David Farrell

David是一位专业程序员,他经常发推文博客关于代码和编程的艺术。

浏览他们的文章

反馈

这篇文章有什么问题吗?请通过在GitHub上打开问题或提交拉取请求来帮助我们。