关于缺乏优化,您必须了解的另一件事是它是复合的。Rakudo 的很大一部分是用Perl 6编写的。因此,例如,[+]
运算符由方法Any.reduce
(使用$expression
set to调用&infix:<+>
)实现,该方法具有内部循环
for @.list {
@args.push($_);
if (@args == $arity) {
my $res = $expression.(@args[0], @args[1]);
@args = ($res);
}
}
换句话说,reduce 的纯 perl 实现,它本身由 Rakudo 运行。因此,不仅您可以看到的代码没有得到优化,而且您没有看到使您的代码运行的代码也没有得到优化。甚至+
操作符的实例实际上也是方法调用,因为虽然+
操作符 onNum
是由 Parrot 实现的,但 Rakudo 中还没有识别出你有两个Num
s 并优化方法调用,所以在 Rakudo 找到之前有一个完整的动态调度multi sub infix:<+>(Num $a, Num $b)
并意识到它真正在做的只是一个“添加”操作码。这是比 Perl 5 慢 100-1000 倍的合理借口 :)
2010 年 8 月 23 日更新
来自 Jonathan Worthington 的更多信息,关于 Perl 6 对象模型(或至少 Rakudo 的概念)需要发生的各种变化,以使事情变得快速,同时保持 Perl 6 的“一切都是方法调用”的性质。
2019 年 1 月 10 日更新
因为我可以看到这仍然受到关注……多年来,Rakudo/MoarVM 已经获得了 JIT、内联、动态专业化以及许多人优化系统每个部分的大量工作。结果是这些方法调用中的大多数都可以“编译出来”并且运行时成本几乎为零。Perl 6 在许多基准测试中的得分比 2010 年快了数百或数千倍,在某些情况下它比 Perl 5 还要快。
在问题开始的 sum-to-100,000 问题的情况下,Rakudo 2018.06 仍然比 perl 5.26.2 慢一点:
$ time perl -e 'use List::Util 'sum'; print sum(1 .. 100000), "\n";' >/dev/null
real 0m0.023s
user 0m0.015s
sys 0m0.008s
$ time perl6 -e 'say [+] 1 .. 100000;' >/dev/null
real 0m0.089s
user 0m0.107s
sys 0m0.022s
但是,如果我们通过运行代码 10,000 次来摊销启动成本,我们会看到一个不同的故事:
$ time perl -e 'use List::Util 'sum'; for (1 .. 10000) { print sum(1 .. 100000), "\n"; }' > /dev/null
real 0m16.320s
user 0m16.317s
sys 0m0.004s
$ time perl6 -e 'for 1 .. 10000 { say [+] 1 .. 100000; }' >/dev/null
real 0m0.214s
user 0m0.245s
sys 0m0.021s
perl6 在启动和编译时比 perl5 多使用几百毫秒,但随后它计算出如何以大约 70 倍的速度进行实际求和。