c++记录std::reduce使用上的问题-意料之外的并行计算
std::reduce的并行执行特性可能导致在不同编译器或不同的运行环境下出现不同的结果,特别是当归约操作(如乘法)不是关联的时,或者编译器对并行算法的支持和优化水平不同时。非确定性的并行执行: std::reduce允许并行执行,这意味着操作的顺序可能因执行而异,特别是在涉及有状态的lambda表达式(如在你的代码中使用&[&](int init, int value))时。std::accum
测试代码
class Solution {
public:
vector<int> statisticalResult(const vector<int>& arrayA) {
int zero = count(arrayA.begin(), arrayA.end(), 0);
int mulitySum = reduce(arrayA.begin(), arrayA.end(), 1, [&](int init, int value) {
cout << init << "\t" << value << endl;
if (value != 0) {
init *= value;
}
return init;
});
vector<int> ans(arrayA.size(), 0);
if (zero >= 2) {
return ans;
}
for (int i = 0; i < arrayA.size(); i++) {
if (zero == 0) {
ans[i] = mulitySum / arrayA[i];
} else if (zero == 1) {
if (arrayA[i] == 0) {
ans[i] = mulitySum;
break;
}
}
}
cout << mulitySum << endl;
return ans;
}
};
int main() {
auto x = Solution{}.statisticalResult({1,2, 0, 4, 5});
for (auto i : x) {
cout << i << " ";
}
}
遇到的问题
上面的代码在不同的编译器上的结果不一样
原因探究
代码中的行为不一致可能由于几个原因导致,但主要问题在于std::reduce函数的使用上。std::reduce是C++17引入的并行算法的一部分,其目的是为了提供一种执行归约操作(比如求和、求积等)的并行化方法。然而,std::reduce的并行执行特性可能导致在不同编译器或不同的运行环境下出现不同的结果,特别是当归约操作(如乘法)不是关联的时,或者编译器对并行算法的支持和优化水平不同时。
非确定性的并行执行: std::reduce允许并行执行,这意味着操作的顺序可能因执行而异,特别是在涉及有状态的lambda表达式(如在你的代码中使用&[&](int init, int value))时。这可能导致在不同的运行中或在不同编译器上出现不一致的结果。
初始值的使用: 你使用1作为初始值开始乘法运算。这在大多数情况下是可行的,但与并行化的归约操作结合使用时,初始值的处理方式可能会影响结果,尤其是在分割任务并在并行任务中使用初始值时。
解决这些不一致性的方法
避免使用std::reduce进行有副作用或状态依赖的操作:如果你想保持代码的确定性,特别是在乘积中包含0的情况下,考虑使用std::accumulate代替std::reduce。std::accumulate保证了操作的顺序,虽然它可能不会像std::reduce那样利用并行执行的优势。
明确处理整数溢出:如果你预计乘积会非常大,可能需要采取措施来避免或处理整数溢出,比如使用更大范围的整数类型或在适当的时候进行模运算。
代码审查和测试:在多个编译器和配置下测试代码,以确保其行为的一致性。这包括在开启和关闭编译器优化的情况下测试,以及使用不同版本的C++标准库。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)