每次下载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
的答案。一切都是作为方法调用处理的,更直接地回答为什么[+]
变慢,以便得到它。
答案 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倍的合理借口:)
More information from Jonathan Worthington关于Perl 6对象模型(或至少是Rakudo的概念)需要发生的变化,以便在保留Perl 6的“一切都是方法调用”性质的同时快速完成任务。
因为我可以看到这仍然受到关注......多年来,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 competition。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
延迟添加:崩溃已在Why do I get 'divide by zero` errors when I try to run my script with Rakudo?处理。 Rakudo计划效率低下,请参阅comments below和http://justrakudoit.wordpress.com/2010/06/30/rakudo-and-speed/。