我对 Raku 很陌生,我对函数式方法有疑问,尤其是 reduce。我最初有这样的方法:
sub standardab{
my $mittel = mittel(@_);
my $foo = 0;
for @_ {
$foo += ($_ - $mittel)**2;
}
$foo = sqrt($foo/(@_.elems));
}
Run Code Online (Sandbox Code Playgroud)
它工作正常。然后我开始使用reduce:
sub standardab{
my $mittel = mittel(@_);
my $foo = 0;
$foo = @_.reduce({$^a + ($^b-$mittel)**2});
$foo = sqrt($foo/(@_.elems));
}
Run Code Online (Sandbox Code Playgroud)
我的执行时间增加了一倍(我将其应用于大约 1000 个元素)并且解决方案相差 0.004(我猜是舍入误差)。如果我正在使用
.race.reduce(...)
Run Code Online (Sandbox Code Playgroud)
我的执行时间比原始顺序代码高 4 倍。有人能告诉我这是什么原因吗?我考虑了并行初始化时间,但是 - 正如我所说 - 我将它应用于 1000 个元素,如果我更改代码中的其他 for 循环以减少它会变得更慢!
谢谢你的帮助
在一般情况下,reduce并for做不同的事情,他们在你的代码做不同的事情。例如,与您的for代码相比,您的reduce代码涉及传递的参数数量是原来的两倍,并且迭代次数减少了一次。我认为这可能是0.004差异的根源。
即使您for和reduce代码做了同样的事情,此类reduce代码的优化版本也永远不会比等效for代码的同等优化版本快。
我认为由于的性质,race这不会自动并行化。(尽管我根据您和 @user0721090601 的评论看到我错了。)但这会产生开销 - 目前很多。reducereduce
如果稍微重写,您可以使用race并行化您的for循环。那可能会加快速度。
for和reduce代码的区别这是我的意思的区别:
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)
Run Code Online (Sandbox Code Playgroud)
有关其操作的更多详细信息,请参阅各自的文档 ( for, reduce)。
您尚未共享您的数据,但我认为for和/或reduce计算涉及Nums(浮点数)。浮点数的添加不是可交换的,因此如果添加最终以不同的顺序发生,您很可能会得到(通常很小)差异。
我认为这可以解释0.004差异。
reduce比你的慢 2 倍for我的执行时间翻了一番(我将其应用于大约 1000 个元素)
首先,您的reduce代码是不同的,如上所述。存在一般的抽象差异(例如,每次调用采用两个参数而不是for块的参数),也许您的特定数据会导致基本的数值计算差异(也许您的for循环计算主要是整数或浮点数学,而您reduce主要是理性的?)。这可能解释了执行时间差异,或其中的一部分。
它的另一部分可能是,一方面, a 之间的区别,reduce默认情况下会编译成闭包的调用,具有调用开销,每个调用有两个参数,以及存储中间结果的临时内存,另一方面, afor将默认编译为直接迭代,{...}只是内联代码而不是闭包调用。(也就是说,reduce有时可能会编译为内联代码;对于您的代码,它甚至可能已经是这种方式。)
更一般地说,Rakudo 优化工作仍处于相对早期的阶段。其中大部分是通用的,加速了所有代码。在努力应用于特定结构的地方,迄今为止最广泛使用的结构已经引起了人们的注意,并且for被广泛使用和reduce较少使用。因此,部分或全部差异可能只是reduce优化不佳。
reduce与race我的执行时间 [for
.race.reduce(...)] 比原始顺序代码高 4 倍
我不认为reduce会自动与race. 根据其 doc,reduce通过“迭代应用知道如何组合两个值的函数”来工作,并且每次迭代中的一个参数是前一次迭代的结果。所以在我看来它必须按顺序完成。
(我在评论中看到我误解了编译器可以通过减少做什么。也许这是一个交换操作?)
总之,您的代码会产生raceing 的开销而没有获得任何好处。
race一般假设您正在使用一些可与race.
首先,正如您所指出的,race会产生开销。将有初始化和拆卸成本,至少其中一些是为正在raced的整体语句/表达式的每次评估重复支付的。
其次,至少就目前而言,这race意味着使用在 CPU 内核上运行的线程。对于某些有效载荷,尽管有任何初始化和拆卸成本,但仍能产生有用的好处。但它充其量只是与内核数量相等的加速。
(有一天,编译器实现者应该有可能发现racedfor循环足够简单,可以在 GPU 而不是 CPU 上运行,然后继续将其发送到 GPU 以实现惊人的加速。)
第三,如果您从字面上写,.race.foo...您将获得赛车某些可调方面的默认设置。默认值几乎可以肯定不是最佳的,可能还有很长的路要走。
当前可调设置为:batch和:degree。有关更多详细信息,请参阅他们的文档。
更一般地说,并行化是否加速代码取决于特定用例的细节,例如使用的数据和硬件。
race与for如果你重写代码多是可以race的for:
$foo = sum do race for @_ { ($_ - $mittel)**2 }
Run Code Online (Sandbox Code Playgroud)
要应用调整,您必须重复race作为一种方法,例如:
$foo = sum do race for @_.race(:degree(8)) { ($_ - $mittel)**2 }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
179 次 |
| 最近记录: |