gpt4 book ai didi

Raku 并行/函数方法

转载 作者:行者123 更新时间:2023-12-03 13:31:36 31 4
gpt4 key购买 nike

我对 Raku 很陌生,我对函数式方法有疑问,尤其是 reduce。
我最初有这样的方法:

sub standardab{
my $mittel = mittel(@_);
my $foo = 0;
for @_ {
$foo += ($_ - $mittel)**2;
}
$foo = sqrt($foo/(@_.elems));
}

它工作正常。然后我开始使用reduce:
sub standardab{
my $mittel = mittel(@_);
my $foo = 0;
$foo = @_.reduce({$^a + ($^b-$mittel)**2});
$foo = sqrt($foo/(@_.elems));
}

我的执行时间增加了一倍(我将其应用于大约 1000 个元素)并且解决方案相差 0.004(我猜是舍入误差)。
如果我正在使用
.race.reduce(...)

我的执行时间比原始顺序代码高 4 倍。
有人能告诉我这是什么原因吗?
我考虑了并行初始化时间,但是 - 正如我所说 - 我将它应用于 1000 个元素,如果我更改代码中的其他 for 循环以减少它会变得更慢!

谢谢你的帮助

最佳答案

概括

  • 一般来说,reducefor 做不同的事情,它们在你的代码中做不同的事情。例如,与您的 for 代码相比,您的 reduce 代码涉及传递的参数数量是原来的两倍,并且迭代次数减少了一次。我认为这很可能是 0.004 差异的根源。
  • 即使您的 forreduce 代码做了同样的事情,这种 reduce 代码的优化版本永远不会比等效 for 代码的同等优化版本更快。
  • 我认为 race 没有自动并行化 reduce 由于 reduce 的性质。 (虽然我看到你和@user0721090601 的评论我错了。)但这会产生开销——目前很多。
  • 如果稍微重写,您可以使用 race 来并行化 for 循环。那可能会加快速度。

  • 关于您的 forreduce 代码之间的区别

    这是我的意思的区别:
    say do for    <a b c d>  { $^a }       # (a b c d)      (4 iterations)

    say do reduce <a b c d>: { $^a, $^b } # (((a b) c) d) (3 iterations)

    有关其操作的更多详细信息,请参阅各自的文档( for reduce )。

    您尚未共享数据,但我假设 for 和/或 reduce 计算涉及 Num s(浮点数)。浮点数的添加不是可交换的,因此如果添加最终以不同的顺序发生,您很可能会得到(通常很小)差异。

    我认为这可以解释 0.004 的差异。

    在您的顺序 reduce 上比您的 for 慢 2 倍

    my execution time doubled (I am applying this to roughly 1000 elements)



    首先,如上所述,您的 reduce 代码是不同的。有一般的抽象差异(例如,每次调用采用两个参数而不是 for 块的一个),也许您的特定数据会导致基本的数值计算差异(也许您的 for 循环计算主要是整数或浮点数学,而您的 reduce 主要是有理数?) .这可能解释了执行时间差异,或其中的一部分。

    它的另一部分可能是一方面 reduce 之间的区别,默认情况下它将编译为闭包的调用,具有调用开销,每个调用有两个参数,以及存储中间结果的临时内存,以及,另外,一个 for 将默认编译为直接迭代,其中 {...} 只是内联代码而不是闭包的调用。 (也就是说,有时 reduce 可能会编译为内联代码;对于您的代码,它甚至可能已经是这种方式。)

    更一般地说,Rakudo 优化工作仍处于相对早期的阶段。其中大部分是通用的,加速了所有代码。在对特定结构进行了努力的情况下,迄今为止最广泛使用的结构已经引起了人们的注意, for 被广泛使用,而 reduce 则较少使用。因此,部分或全部差异可能只是 reduce 优化不佳。

    reducerace

    my execution time [for .race.reduce(...)] is 4 times higher than with the original sequential code



    我不认为 reduce 会自动与 race 并行化。根据 its docreduce 通过“迭代应用知道如何组合两个值的函数”来工作,并且每次迭代中的一个参数是前一次迭代的结果。所以在我看来它必须按顺序完成。

    (我在评论中看到我误解了编译器可以通过减少做什么。也许这是一个交换操作?)

    总之,您的代码会产生 race ing 的开销而没有获得任何好处。

    一般在 race
    假设您正在使用一些可与 race 并行的操作。

    首先,正如您所指出的, race 会产生开销。将有初始化和拆卸成本,至少其中一些是为 race d 的整体语句/表达式的每次评估重复支付的。

    其次,至少就目前而言, race 表示使用在 CPU 内核上运行的线程。对于某些有效载荷,尽管有任何初始化和拆卸成本,但仍能产生有用的好处。但它充其量只是与内核数量相等的加速。

    (有一天,编译器实现者应该有可能发现 race d for 循环足够简单,可以在 GPU 而不是 CPU 上运行,然后将其发送到 GPU 以实现惊人的加速。)

    第三,如果您真的写 .race.foo...,您将获得赛车某些可调方面的默认设置。默认值几乎肯定不是最佳的,可能还有很长的路要走。

    当前可调设置为 :batch:degree 。有关更多详细信息,请参阅 their doc

    更一般地说,并行化是否加速代码取决于特定用例的细节,例如使用的数据和硬件。

    使用 racefor
    如果您稍微重写一下代码,您可以将 race 改为 for :
    $foo = sum do race for @_ { ($_ - $mittel)**2 } 

    要应用调整,您必须重复 race 作为一种方法,例如:
    $foo = sum do race for @_.race(:degree(8)) { ($_ - $mittel)**2 } 

    关于Raku 并行/函数方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61392026/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com