为什么 std::reduce 需要交换性?
Why does std::reduce need commutativity?
https://en.cppreference.com/w/cpp/algorithm/reduce
它说如果操作不是可交换的,则不定义操作的行为,但为什么呢?我们只是将数组划分为块,然后合并结果。是否只需要具有关联性?
std::reduce
需要结合性和交换性。并行算法显然需要关联性,因为您希望在单独的块上执行计算,然后将它们组合在一起。
至于交换性:根据MSVC STL开发人员Billy O'Neal的reddit帖子,这是允许矢量化到SIMD指令所必需的:
交换性对于启用矢量化也是必要的,因为您希望reduce的代码如下所示:
vecRegister = load_contiguous(first); while (a vector register sized chunk is left) { first += packSize; vecRegister = add_packed(load_contiguous(first), vecRegister); } // combine vecRegister's packed components
等等,其中给定整数和 SSE 寄存器和
a * b * c * d * e * f * g * h 给出类似 (a * e( * (b * f( * (c * g( * (d * h( 的东西。大多数其他语言并没有做明确的事情来使矢量化它们的减少成为可能。没有什么说如果有人提出一个引人注目的用例,我们将来不能添加一个noncommutative_reduce或类似的东西。
如果操作数之间的操作不是可交换的,则行为实际上是不确定的。"非确定性"与"未定义"不同。例如,浮点数学不是可交换的。这就是为什么对std::reduce
的调用可能不是确定性的,因为二进制函数以未指定的顺序应用。
请参阅标准中的此注释:
注意:
reduce
和accumulate
之间的区别在于,binary_op
在 未指定顺序,它为非关联或非交换产生非确定性结果 binary_op,例如浮点加法。—尾注 ]
该标准将广义和定义如下: numeric.defns
定义GENERALIZED_NONCOMMUTATIVE_SUM(op, a1, ..., aN(,如下所示:
当 N 为 1 时为 a1,否则
op(GENERALIZED_NONCOMMUTATIVE_SUM(op, a1, ..., aK(, op(GENERALIZED_NONCOMMUTATIVE_SUM(op, aM, ..., aN(( 对于任何 K 其中 1
将GENERALIZED_SUM(op, a1, ..., aN( 定义为 GENERALIZED_NONCOMMUTATIVE_SUM(op, b1, ..., bN(,其中 b1, ...,bN 可以是 a1, ..., aN 的任何排列。
因此,求和的顺序以及操作数的顺序是未指定的。因此,如果二进制运算不是可交换的或非关联的,则结果是未指定的。
这里也明确指出了这一点。
关于原因:它给了图书馆供应商更多的自由,所以他们可能会也可能不会更好地实施它。作为实现可以从交换性中受益的示例。考虑a+b+c+d+e
和,我们首先并行计算a+b
和c+d
。现在a+b
c+d
之前返回(因为它可能发生,因为它是并行完成的(。我们现在可以直接计算(a+b)+e
,然后将此结果添加到c+d
的结果中,而不是等待c+d
的返回值。所以最后我们计算了((a+b)+e)+(c+d)
,这是a+b+c+d+e
的重排。
为什么
std::reduce
需要交换性?
为了速度。
如果运算符是可交换的,则可以重新排列运算顺序而不影响结果。
如果你能重新排列操作的顺序,你可以让不同的线程,或者进程,或者硬件加速器或者什么的——对一些要执行的操作独立工作,而不关心它们完成部分总和的顺序,也不关心它们内部的操作顺序,然后最后以任何方便的方式将部分总和相加。
- 如何使 std::sort 在 std::swap 和我的命名空间的模板化交换之间没有名称冲突?
- 为什么 std::reduce 需要交换性?
- std::vector move 而不是交换到空 vector 并释放存储
- 为什么只有当我的容器有超过 32 个元素时才由 std::sort 调用交换?
- 使用 std::vector::swap 方法在C++中交换两个不同的向量是否安全?
- 如何将 std::vector 索引交换到<int>数据,将数据交换到索引
- libstdc++ 中 std:shared_ptr 的原子交换如何正确
- 有没有办法在 std::list 中交换节点?
- 如果我在整个班级上使用std ::交换,则会使用专门的shared_ptr :: swap()函数
- std::在 std::shared_ptr 之间交换,<A>其中 A 具有动态数组
- 尝试使用 {} 和 std::make_pair() 交换两个变量时的行为不同
- 为什么使用std::swap不会在外部范围中隐藏其他交换函数
- 了解std ::交换指针和std :: vectors
- 使用带有模板参数的交换与 std::vector 包含冲突
- 交换 std::list 中的相邻元素
- 你能用lambda比较器交换std::队列吗
- 如何正确地多次交换std::函数
- 用用户实现的交换来交换std::函数
- 交换std::vector作为函数参数
- 在恒定时间内交换std::vector的内容-是否可能