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

时间:2010-06-28 19:52:00

标签: performance perl perl6 rakudo raku

每次下载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的答案。一切都是作为方法调用处理的,更直接地回答为什么[+]变慢,以便得到它。

5 个答案:

答案 0 :(得分:22)

为什么Rakudo如此缓慢有各种各样的原因。

第一个也许是最重要的原因是Rakudo还没有进行任何优化。目前的目标是更多探索新功能,并变得更加强大。你知道,他们说“首先使它运行,然后使其正确,然后快速运行”。

第二个原因是parrot还没有提供任何JIT编译,垃圾收集器也不是最快的。有一个JIT编译器的计划,人们正在研究它(之前的一个被删除,因为它只是i386和维护噩梦)。还有将Rakudo移植到其他虚拟机的想法,但这肯定会等到7月底之后。

最后,没有人能够真正地告诉我们有一个完整的,经过优化的Perl 6实现的速度有多快,但我确实认为它比现在要好得多。

BTW你引用[+] 1..$big_number的情况可以在O(1)中运行,因为1..$big_number返回一个范围,这是内省的。因此,您可以使用[+] Range案例的总和公式。再一次,这是可以完成的事情,但尚未完成。

答案 1 :(得分:17)

您必须了解缺少优化的另一件事是它是复合。 Rakudo的很大一部分是在Perl 6 中编写的。例如,[+]运算符由方法Any.reduce实现(调用$expression设置为&infix:<+>),其内部循环为

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

换句话说,一个pure-perl实现reduce,它本身由Rakudo运行。因此,您看到的代码不仅没有得到优化,而且看到让代码运行的代码也没有得到 优化。甚至+运算符的实例实际上都是方法调用,因为尽管+上的Num运算符是由Parrot实现的,但是在Rakudo中还没有任何东西可以识别出你有两个{{} 1}}并优化掉方法调用,因此在Rakudo找到Num之前有一个完整的动态调度,并意识到它所做的一切都是'添加'操作码。这是比Perl 5慢100-1000倍的合理借口:)

2010年8月23日更新

More information from Jonathan Worthington关于Perl 6对象模型(或至少是Rakudo的概念)需要发生的变化,以便在保留Perl 6的“一切都是方法调用”性质的同时快速完成任务。

更新1/10/2019

因为我可以看到这仍然受到关注......多年来,Rakudo / MoarVM已经获得了JIT,内联,动态专业化和的工作,许多人优化了每一部分系统。结果是大多数方法调用可以“编译出来”并且运行时成本几乎为零。 Perl 6在许多基准测试中的分数比2010年高出数百或数千倍,在某些情况下它比Perl 5快。

在问题开始的总和到100,000的问题的情况下,Rakudo 2018.06仍然比perl 5.26.2慢一点:

multi sub infix:<+>(Num $a, Num $b)

但如果我们通过运行代码10,000次来分摊启动成本,我们会看到一个不同的故事:

$ 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
在启动和编译时,

perl6比perl5使用了几百毫秒,但它确定了如何将实际求和速度提高了大约70倍。

答案 2 :(得分:5)

当然不是因为一切都是对象,因为在许多其他语言中也是如此(如Ruby)。 Perl 6没有理由比Perl 5或Ruby等其他语言的速度慢,但事实是Rakudo并不像perl或CRuby那样成熟。目前还没有太多的速度优化。

答案 3 :(得分:4)

考虑到现在你的测试用例是optimized to an O(1) algorithm几乎立即返回,而且看起来几乎就像每周有几次优化;
我期望在各方面都有相当的性能提升。

$ 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

答案 4 :(得分:3)

我于2008年12月将这些内容提交给Fefe's language competitionwp.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

延迟添加:崩溃已在Why do I get 'divide by zero` errors when I try to run my script with Rakudo?处理。 Rakudo计划效率低下,请参阅comments belowhttp://justrakudoit.wordpress.com/2010/06/30/rakudo-and-speed/