std :: partial_sum和std :: inclusive_scan之间的区别是什么?

时间:2016-06-24 05:23:10

标签: c++ numeric stl-algorithm c++17

在阅读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 );

有人可以详细说明他们之间的差异吗?我何时会选择一个而不是另一个?

2 个答案:

答案 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_opstd::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_suminclusive_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(结合非顺序执行策略)时,必须牢记这一点。

实际上,浮点运算足够接近以至于可以被认为是关联的。如果操作顺序不固定,甚至可以提高精度。这意味着简单的顺序算法无论如何都不是完美的。