c++ std::partial_sum和std::inclusive_scan有什么区别?

23c0lvtd  于 2023-11-19  发布在  其他
关注(0)|答案(3)|浏览(136)

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


有人能详细说明他们的区别吗?我什么时候会选择其中一个?

yduiuuwa

yduiuuwa1#

std::inclusive_scan的文档说明:
换句话说,求和运算可以以任意顺序执行,如果binary_op不是关联的,则行为是不确定的。
std::partial_sum的文档毫无保留地指出:

*(d_first+k) = *first + *(first+1) + ... + *(first+k);

字符串
因此,std::inclusive_scan仅在binary_op是关联的时才等价于std::partial_sum,即当(aopb)opc = aop(bopc)时。
在非关联binary_op的情况下,std::partial_sum将产生确定性结果,而您不知道std::inclusive_scan会产生什么结果。

f4t66c6m

f4t66c6m2#

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,否则,它将是未定义的行为。

如果没有用户指定的操作符,我可以总是用inclusive_scan替换partial_sum吗?换句话说,将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(与非顺序执行策略组合)时必须记住这一点。
尽管如此,在实践中,浮点运算足够接近被认为是关联的。如果运算的顺序不固定,精度甚至可以提高。这意味着,直接的顺序算法无论如何都不是完美的。

ecbunoof

ecbunoof3#

这几乎是相同的,但包容性扫描可以并行运行。相同的界面。

相关问题