随着 Perl 6 实现的成熟,我们可以期待哪些性能提升?
每次我下载 Rakudo Perl 6 的新副本时,我都会运行以下表达式,只是为了了解其当前的性能:
say [+] 1 .. 100000;
速度一直在增加,但每次都会有明显的延迟(几秒钟)计算。作为比较,类似的东西在 Perl 5(或其他解释性语言)中几乎立即返回:
use List::Util 'sum';
print sum(1 .. 100000), "\n";
或者在 Ruby 中(也几乎立即):
(1 .. 100000).inject(0) {|sum,x| sum+x}
将表达式重写为 Perl6 loop
最终速度大约是原来的两倍减少范围,但对于简单的计算来说仍然是一个非常明显的延迟(超过一秒):
my $sum;
loop (my $x = 1; $x <= 100000; $x++) {$sum += $x}
所以我的问题是,Perl6 实现的哪些方面导致了这些性能问题?这应该随着时间的推移而改善,还是这种开销是 Perl6 使用的“一切都是对象”模型的不幸的副作用?
最后,loop
构造比 [+]
归约运算符更快?我认为循环会导致比减少更多的总操作。
编辑:
如果可以的话,我会接受 mortiz
和 hobbs
的答案。一切都作为方法调用来处理,更直接地回答了为什么 [+]
速度慢,这样人们就明白了。
Each time I have downloaded a new copy of Rakudo Perl 6, I have run the following expression just to get an idea of its current performance:
say [+] 1 .. 100000;
And the speeds have been increasing, but each time, there is a noticeable delay (several seconds) for the calculation. As a comparison, something like this in Perl 5 (or other interpreted languages) returns almost instantly:
use List::Util 'sum';
print sum(1 .. 100000), "\n";
or in Ruby (also nearly instant):
(1 .. 100000).inject(0) {|sum,x| sum+x}
Rewriting the expression as a Perl6 loop
ends up being about twice as fast as reducing the range, but it is still a very noticeable delay (more than a second) for the simple calculation:
my $sum;
loop (my $x = 1; $x <= 100000; $x++) {$sum += $x}
So my question is, what aspects of the Perl6 implementation are causing these performance issues? And should this improve with time, or is this overhead an unfortunate side effect of the "everything is an object" model that Perl6 is using?
And lastly, what about the loop
construct is faster than the [+]
reduction operator? I would think that the loop would result in more total ops than the reduction.
EDIT:
I'd accept both mortiz
's and hobbs
's answers if I could. That everything is a being handled as a method call more directly answers why [+]
is being slow, so that one gets it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
Rakudo 如此缓慢的原因确实有多种。
第一个或许也是最重要的原因是 Rakudo 尚未进行任何优化。当前的目标是更多地探索新功能,并变得更加健壮。你知道,他们说“首先让它运行,然后让它正确,然后让它快速”。
第二个原因是 parrot 还不提供任何 JIT 编译,而且垃圾收集器也不是最快的。有 JIT 编译器的计划,人们正在研究它(之前的编译器被删除了,因为它只是 i386 并且是维护的噩梦)。还有将 Rakudo 移植到其他虚拟机的想法,但这肯定要等到 7 月底之后。
最后,在我们拥有一个完整的、优化良好的 Perl 6 实现之前,没有人能真正知道它的速度有多快,但我确实希望它会比现在好得多。
顺便说一句,您引用的情况
[+] 1..$big_number
可以在 O(1) 中运行,因为1..$big_number
返回一个范围,即值得反省的。因此,您可以对[+] Range
情况使用求和公式。这又是一件可以做但尚未完成的事情。There are really various reasons why Rakudo is so slow.
The first and maybe most important reason is that Rakudo doesn't do any optimizations yet. The current goals are more explore new features, and to become more robust. You know, they say "first make it run, then make it right, then make it fast".
The second reason is that parrot doesn't offer any JIT compilation yet, and the garbage collector isn't the fastest. There are plans for a JIT compiler, and people are working on it (the previous one was ripped out because it was i386 only and a maintenance nightmare). There are also thoughts of porting Rakudo to other VMs, but that'll surely wait till after end of July.
In the end, nobody can really tell how fast a complete, well-optimized Perl 6 implementation will be until we have one, but I do expect it to be much better than now.
BTW the case you cited
[+] 1..$big_number
could be made to run in O(1), because1..$big_number
returns a Range, which is introspectable. So you can use a sum formula for the[+] Range
case. Again it's something that could be done, but that hasn't been done yet.这当然不是因为一切都是对象,因为在许多其他语言中也是如此(例如 Ruby)。 Perl 6 没有理由比 Perl 5 或 Ruby 等其他语言慢很多,但事实是 Rakudo 并不像 Perl 或 CRuby 那样成熟。目前还没有太多的速度优化。
It certainly isn't because everything is an object, because that's true in a number of other languages too (like Ruby). There's no reason why Perl 6 would have to be magnitudes slower than other languages like Perl 5 or Ruby, but the fact is that Rakudo is not as mature as perl or CRuby. There hasn't been much speed optimization yet.
考虑到现在您的测试用例已优化为O(1)算法< /a> 几乎立即返回,并且看起来几乎每周都有几次优化;
我预计各方面的性能都会有相当大的提高。
即使这不是范围的特殊情况,它仍然比以前快很多。
现在,它可以在不到五分之一秒的时间内完成测试计算。
Considering that now your test case is optimized to an O(1) algorithm that returns nearly instantly, and that it seems almost like there are several optimizations a week;
I expect quite an performance improvement all around.
Even if that wasn't special-cased for ranges it is still quite a bit faster than it was.
It now does your test calculation in less than a fifth of a second.
我于 2008 年 12 月向 Fefe 语言竞赛提交了这些内容。
wp.pugs.pl
是 Perl 5 示例的直译,wp.rakudo.pl
更加复杂。我有两个程序,因为这两个程序实现了规范的不同子集。同时构建信息已经过时。来源:这些是 2008 年的结果:
今天:
后期添加:崩溃已在 为什么我会收到“除以零”错误当我尝试使用 Rakudo 运行我的脚本时?。 Rakudo 程序效率低下,请参阅下面的评论和http://justrakudoit.wordpress.com/2010/06/30/rakudo-and-speed/。
I submitted these to Fefe's language competition in December 2008.
wp.pugs.pl
is a literal translation of the Perl 5 example,wp.rakudo.pl
is far more sixier. I have two programs because the two implement a different subset of the spec. Build information is outdated meanwhile. The sources:These were the results in 2008:
Today:
Late additions: The crash has been dealt with at Why do I get 'divide by zero` errors when I try to run my script with Rakudo?. The Rakudo program is inefficient, see comments below and http://justrakudoit.wordpress.com/2010/06/30/rakudo-and-speed/.
关于缺乏优化,您必须了解的另一件事是它是复合的。 Rakudo 的很大一部分是用 Perl 6 编写的。例如,
[+]
运算符是通过方法Any.reduce
实现的(通过将$expression
设置为&infix 来调用) :<+>
),换句话说,它的内部循环是一个纯 Perl 的 reduce 实现,它本身由 Rakudo 运行。因此,不仅您可以看到的代码没有得到优化,您看不到使您的代码运行的代码也没有得到优化
优化。即使
+
运算符的实例实际上也是方法调用,因为尽管Num
上的+
运算符是由 Parrot 实现的,但 Rakudo 中还没有任何东西可以认识到您有两个Num
并优化了方法调用,因此在 Rakudo 找到multi sub 中缀之前有一个完整的动态调度:<+>(Num $a, Num $b)
并意识到它真正做的只是一个“添加”操作码。这是比 Perl 5 慢 100-1000 倍的合理借口:)更新 8/23/2010
Jonathan Worthington 提供的更多信息,涉及 Perl 6 对象模型(或至少 Rakudo 的概念)需要进行的各种更改,以便在保持速度的同时加快速度Perl 6 的“一切都是方法调用”的本质。
更新 1/10/2019
因为我可以看到这仍然受到关注...多年来,Rakudo/MoarVM 已经获得了 JIT、内联、动态专门化以及许多人优化的大量工作系统的每个部分。结果是,大多数方法调用都可以“编译出来”,并且运行时成本几乎为零。 Perl 6 在许多基准测试中的得分比 2010 年快数百或数千倍,并且在某些情况下比 Perl 5 更快。
就问题开始时的 sum-to-100,000 问题而言,Rakudo 2018.06 仍然是一个比 perl 5.26.2 慢一点:
但是如果我们通过运行代码 10,000 次来分摊启动成本,我们会看到不同的情况:
perl6 在启动和编译时比 perl5 多用了几百毫秒,但随后它会弄清楚如何做实际求和速度快了大约 70 倍。
Another thing you have to understand about the lack of optimization is that it's compounded. A large portion of Rakudo is written in Perl 6. So for example the
[+]
operator is implemented by the methodAny.reduce
(called with$expression
set to&infix:<+>
), which has as its inner loopin other words, a pure-perl implementation of reduce, which itself is being run by Rakudo. So not only is the code you can see not getting optimized, the code that you don't see that's making your code run is also not getting
optimized. Even instances of the
+
operator are actually method calls, since although the+
operator onNum
is implemented by Parrot, there's nothing yet in Rakudo to recognize that you've got twoNum
s and optimize away the method call, so there's a full dynamic dispatch before Rakudo findsmulti sub infix:<+>(Num $a, Num $b)
and realizes that all it's really doing is an 'add' opcode. It's a reasonable excuse for being 100-1000x slower than Perl 5 :)Update 8/23/2010
More information from Jonathan Worthington on the kinds of changes that need to happen with the Perl 6 object model (or at least Rakudo's conception of it) to make things fast while retaining Perl 6's "everything is method calls" nature.
Update 1/10/2019
Since I can see that this is still getting attention... over the years, Rakudo/MoarVM have gotten JIT, inlining, dynamic specialization, and tons of work by many people optimizing every part of the system. The result is that most of those method calls can be "compiled out" and have nearly zero runtime cost. Perl 6 scores hundreds or thousands of times faster on many benchmarks than it did in 2010, and in some cases it's faster than Perl 5.
In the case of the sum-to-100,000 problem that the question started with, Rakudo 2018.06 is still a bit slower than perl 5.26.2:
But if we amortize out startup cost by running the code 10,000 times, we see a different story:
perl6 uses a few hundred more milliseconds than perl5 on startup and compilation, but then it figures out how to do the actual summation around 70 times faster.