随着 Perl 6 实现的成熟,我们可以期待哪些性能提升?

发布于 2024-09-07 00:22:33 字数 821 浏览 7 评论 0原文

每次我下载 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 构造比 [+] 归约运算符更快?我认为循环会导致比减少更多的总操作。

编辑:

如果可以的话,我会接受 mortizhobbs 的答案。一切都作为方法调用来处理,更直接地回答了为什么 [+] 速度慢,这样人们就明白了。

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

清引 2024-09-14 00:22:34

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), because 1..$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.

梦忆晨望 2024-09-14 00:22:34

这当然不是因为一切都是对象,因为在许多其他语言中也是如此(例如 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.

旧人 2024-09-14 00:22:34

考虑到现在您的测试用例已优化为O(1)算法< /a> 几乎立即返回,并且看起来几乎每周都有几次优化;
我预计各方面的性能都会有相当大的提高。

$ perl6 -e 'say [+] 1..10**1000; say now - INIT now'
5000000000000000000000000000000000000000000000 ...
0.007447

即使这不是范围的特殊情况,它仍然比以前快很多。
现在,它可以在不到五分之一秒的时间内完成测试计算。

$ perl6 -e 'say [+] (1..100000).list; say now - INIT now'
5000050000
0.13052975

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.

$ perl6 -e 'say [+] 1..10**1000; say now - INIT now'
5000000000000000000000000000000000000000000000 ...
0.007447

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.

$ perl6 -e 'say [+] (1..100000).list; say now - INIT now'
5000050000
0.13052975
夜夜流光相皎洁 2024-09-14 00:22:34

我于 2008 年 12 月向 Fefe 语言竞赛提交了这些内容。 wp.pugs.pl 是 Perl 5 示例的直译,wp.rakudo.pl 更加复杂。我有两个程序,因为这两个程序实现了规范的不同子集。同时构建信息已经过时。来源:

#!/usr/bin/env pugs
# Pugs: <http://pugs.blogs.com/> <http://pugscode.org/>
# prerequisite: ghc-6.8.x, not 6.10.x
# svn co http://svn.pugscode.org/pugs/
# perl Makefile.PL
# make
# if build stops because of haskeline, do:
#   $HOME/.cabal/bin/cabal update ; $HOME/.cabal/bin/cabal install haskeline

# learn more: <http://jnthn.net/papers/2008-tcpw-perl64danoob-slides.pdf>

my %words;

for =<> {
    for .split {
        %words{$_}++
    }
}

for (sort { %words{$^b} <=> %words{$^a} }, %words.keys) {
    say "$_ %words{$_}"
}

#!/usr/bin/env perl6
# Rakudo: <http://rakudo.org/> <http://www.parrot.org/download>
# svn co http://svn.perl.org/parrot/trunk parrot
# perl Configure.pl
# make perl6

# Solution contributed by Frank W. & Moritz Lenz
# <http://use.perl.org/~fw/journal/38055>
# learn more: <http://jnthn.net/papers/2008-tcpw-perl64danoob-slides.pdf>

my %words;

$*IN.lines.split(/\s+/).map: { %words{$_}++ };

for %words.pairs.sort: { $^b.value <=> $^a.value } -> $pair {
    say $pair
}

这些是 2008 年的结果:

$ time ./wp.pugs.pl < /usr/src/linux/COPYING > foo

real    0m2.529s
user    0m2.464s
sys     0m0.064s

$ time ./wp.rakudo.pl < /usr/src/linux/COPYING > foo

real    0m32.544s
user    0m1.920s
sys     0m0.248s

今天:

$ time ./wp.pugs.pl < /usr/src/linux/COPYING > foo

real    0m5.105s
user    0m4.898s
sys     0m0.096s

$ time ./wp.rakudo.pl < /usr/src/linux/COPYING > foo
Divide by zero
current instr.: '' pc -1 ((unknown file):-1)
Segmentation fault

real    0m3.236s
user    0m0.447s
sys     0m0.080s

后期添加:崩溃已在 为什么我会收到“除以零”错误当我尝试使用 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:

#!/usr/bin/env pugs
# Pugs: <http://pugs.blogs.com/> <http://pugscode.org/>
# prerequisite: ghc-6.8.x, not 6.10.x
# svn co http://svn.pugscode.org/pugs/
# perl Makefile.PL
# make
# if build stops because of haskeline, do:
#   $HOME/.cabal/bin/cabal update ; $HOME/.cabal/bin/cabal install haskeline

# learn more: <http://jnthn.net/papers/2008-tcpw-perl64danoob-slides.pdf>

my %words;

for =<> {
    for .split {
        %words{$_}++
    }
}

for (sort { %words{$^b} <=> %words{$^a} }, %words.keys) {
    say "$_ %words{$_}"
}

#!/usr/bin/env perl6
# Rakudo: <http://rakudo.org/> <http://www.parrot.org/download>
# svn co http://svn.perl.org/parrot/trunk parrot
# perl Configure.pl
# make perl6

# Solution contributed by Frank W. & Moritz Lenz
# <http://use.perl.org/~fw/journal/38055>
# learn more: <http://jnthn.net/papers/2008-tcpw-perl64danoob-slides.pdf>

my %words;

$*IN.lines.split(/\s+/).map: { %words{$_}++ };

for %words.pairs.sort: { $^b.value <=> $^a.value } -> $pair {
    say $pair
}

These were the results in 2008:

$ time ./wp.pugs.pl < /usr/src/linux/COPYING > foo

real    0m2.529s
user    0m2.464s
sys     0m0.064s

$ time ./wp.rakudo.pl < /usr/src/linux/COPYING > foo

real    0m32.544s
user    0m1.920s
sys     0m0.248s

Today:

$ time ./wp.pugs.pl < /usr/src/linux/COPYING > foo

real    0m5.105s
user    0m4.898s
sys     0m0.096s

$ time ./wp.rakudo.pl < /usr/src/linux/COPYING > foo
Divide by zero
current instr.: '' pc -1 ((unknown file):-1)
Segmentation fault

real    0m3.236s
user    0m0.447s
sys     0m0.080s

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/.

め七分饶幸 2024-09-14 00:22:33

关于缺乏优化,您必须了解的另一件事是它是复合的。 Rakudo 的很大一部分是用 Perl 6 编写的。例如,[+] 运算符是通过方法 Any.reduce 实现的(通过将 $expression 设置为 &infix 来调用) :<+>),

for @.list {
    @args.push($_);
    if (@args == $arity) {
        my $res = $expression.(@args[0], @args[1]);
        @args = ($res);
    }
}

换句话说,它的内部循环是一个纯 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 慢一点:

$ 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 倍。

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 method Any.reduce (called with $expression set to &infix:<+>), which has as its inner loop

for @.list {
    @args.push($_);
    if (@args == $arity) {
        my $res = $expression.(@args[0], @args[1]);
        @args = ($res);
    }
}

in 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 on Num is implemented by Parrot, there's nothing yet in Rakudo to recognize that you've got two Nums and optimize away the method call, so there's a full dynamic dispatch before Rakudo finds multi 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:

$ 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

But if we amortize out startup cost by running the code 10,000 times, we see a different story:

$ 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 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文