在阅读std::inclusive_scan时,似乎没有任何例子 它让我感到非常相似std::partial_sum。
partial_sum:
template< class InputIt, class OutputIt >
OutputIt partial_sum( InputIt first,
InputIt last, OutputIt d_first );
inclusive_scan:
template< class InputIt, class OutputIt >
OutputIt inclusive_scan( InputIt first,
InputIt last, OutputIt d_first );
有人可以详细说明他们之间的差异吗?我何时会选择一个而不是另一个?
答案 0 :(得分:15)
std::inclusive_scan
州的文档:
换句话说,可以以任意顺序执行求和操作。
如果binary_op
不是关联的,则行为是不确定的。
std::partial_sum
州的文档,没有任何保留:
*(d_first+k) = *first + *(first+1) + ... + *(first+k);
因此,std::inclusive_scan
仅在std::partial_sum
关联时才相当于binary_op
,即当(a
op b)
运算强> c = a
的运算强> (b
的运算强> c)
。
如果是非关联binary_op
,std::partial_sum
会产生确定性结果,而您不知道std::inclusive_scan
会发生什么。
答案 1 :(得分:2)
std::inclusive_scan是C ++ 17中作为并行STD的一部分添加的,而
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
将是相同的。后面的算法是相同的,实际上,“包含扫描”,“部分和”等都是相同类型的计算的同义词(维基百科称其为prefix 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
替换为inclusive_scan
?换句话说,将partial_sum(first, last, out)
更改为inclusive_scan(first, last, out)
是否安全?
通常,std::plus
是关联的(即x + (y + z) == (x + y) + z
将成立)。在这种情况下,更改它是安全的。
尽管有例外。一些奇怪的用户定义类可能会以意外方式重载std::plus
。但是更有趣的情况是浮点运算,它们是not associative in a strict sense:
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
(结合非顺序执行策略)时,必须牢记这一点。
实际上,浮点运算足够接近以至于可以被认为是关联的。如果操作顺序不固定,甚至可以提高精度。这意味着简单的顺序算法无论如何都不是完美的。