std::inclusive_scan 是C++17标准库里并行编程的一部分,而std::partial_sum 则早在此之前就已存在。这两个函数都被重载了。如果您未指定运算符,则默认为 std::plus
:
template< class InputIt, class OutputIt >
OutputIt partial_sum( InputIt first,
InputIt last, OutputIt d_first );
对于许多类型,如整数,其中std::plus
是可结合的,partial_sum
和inclusive_scan
将是相同的。实际上,背后的算法相同,"inclusive scan"、"partial sum"等都是同一类型计算的同义词(维基百科称其为前缀和)。
不过,使用用户指定运算符的其他重载函数存在差异:
template< class InputIt, class OutputIt, class BinaryOperation >
OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first,
BinaryOperation op );
partial_sum
的约束条件比inclusive_scan
弱。它只要求op
不能使任何迭代器失效或修改涉及范围内的任何元素。
并行化的问题在于它不需要op
是可结合的。由于partial_sum
按照其规定需要顺序执行,因此到目前为止还不需要这样做。缺点是它阻止了并行执行,因为你无法重新排序计算。
在inclusive_scan
中,显式要求op
是一个可结合的操作,否则会得到未定义的行为。然而,它的优点在于现在可以通过指定执行策略来更改代码以支持并行执行:
template< class ExecutionPolicy, class ForwardIt1, class ForwardIt2,
class BinaryOperation >
ForwardIt2 inclusive_scan( ExecutionPolicy&& policy,
ForwardIt1 first, ForwardIt1 last,
ForwardIt2 d_first, BinaryOperation binary_op );
什么情况下我应该选择其中一个而不是另一个?
如果没有给出用户指定的运算符,我是否总是可以用inclusive_scan
替换partial_sum
?换句话说,将partial_sum(first, last, out)
更改为inclusive_scan(first, last, out)
是否安全?
通常,std::plus
是可结合的(即,x + (y + z) == (x + y) + z
将成立)。在这种情况下,更改是安全的。
但是有例外情况。一些奇怪的用户定义类可能以意想不到的方式重载std::plus
。但是,浮点数操作是一个更有趣的例子,其在严格意义上不是结合的:
0.1 + (0.2 + 0.3) != (0.1 + 0.2) + 0.3
// could be identical on some architectures, but fails on my machine (x86-64, AMD FX-8370)
如果您的计算需要完全可重现,那么在将partial_sum
更改为inclusive_scan
时(与非顺序执行策略结合使用),必须牢记这一点。
然而,在实践中,浮点运算足够接近可以被认为是可结合的。如果操作的顺序不固定,甚至可以提高精度。也就是说,简单的顺序算法无论如何都不完美。