在Perl 6中是否有一个快速并行的“for”循环?

时间:2017-11-15 07:08:31

标签: concurrency perl6

鉴于一些代码对1到500000之间的每个数字进行了一些数学/转换,我们有选择:

  1. 简单循环:for ^500000 -> $i { my $result = ($i ** 2).Str; }。在我不科学的基准测试中,这需要2.8秒。

  2. 最规范的并行版本在Promise中完成每项工作,然后等待结果。 await do for ^500000 -> $i { start { my $result = ($i ** 2).Str; } }需要19秒。这很慢!创建新的承诺必须有太多的开销才能进行这样简单的计算。

  3. 使用并行map操作相当快。在2.0秒时,操作似乎几乎不足以利用并行化:(^500000).race.map: -> $i { my $result = ($i ** 2).Str; }

  4. 第三种选择似乎最好。不幸的是,它看起来像一个黑客。我们不应该在sink上下文中编写map代码进行迭代,因为在源代码中读取“map”的其他人可能会认为目的是构建一个列表,这根本不是我们的意图。以这种方式使用map的沟通不畅。

    有没有任何规范的快速方法来使用Perl 6的内置并发?如果一个超级运算符可以接受一个块而不仅仅是函数,那么它就是完美的:

    (^500000)».(-> $i { my $result = ($i ** 2).Str; }) # No such method 'CALL-ME' for invocant of type 'Int'
    

2 个答案:

答案 0 :(得分:6)

如果您想使用超级或竞赛操作,则必须拼写为hyper for @blah.hyper(:batch(10_000))race for @blah.race(:batch(10_000))。或者没有参数:hyper for @blahrace for @blah

之所以这样做是因为您可能拥有for some-operation() { some-non-threadsafe-code }之类的代码,其中some-operation是库的一部分。现在你不能再告诉for循环中是否包含线程不安全的代码,即使你知道库在那个时间点没有返回HyperSeq,如果库作者来了怎么办?加上这个好主意,通过加强它来加快some-operation的速度?

这就是为什么在代码所在的位置需要一个“并行运行此for循环的安全”的能力,而不仅仅是创建序列的地方。

答案 1 :(得分:3)

在我的电脑上,这比天真的循环要快一点(~15%):

(^500_000).hyper(batch => 100_000).map(-> $i { my $result = ($i ** 2).Str; })

由于循环内的计算速度非常快,因此并行化和同步的成本通常会使您获得的任何收益相形见绌。唯一的补救措施是批量大。

更新:批量大小为200_000时,我的结果会稍好一些(加快几个百分点)。