使用std::transform实现离散拉普拉斯算子,并行策略下是否有未定义行为?
并行std::transform中指针操作的未定义行为疑问
我需要一个lambda表达式,将(负)离散拉普拉斯算子(矩阵)应用于std::array或std::vector这类连续内存容器。我编写了如下使用std::transform结合指针增减操作的代码,想请教这样的写法是否会触发未定义行为?
auto A = [n,&h2](const auto & in, auto & out) { // First line of the matrix out[0] = (2.*in[0] - in[1])/h2 ; // Middle lines of the matrix std::transform(std::execution::par_unseq, std::next(in.cbegin()),std::next(in.cend(),-1),std::next(out.begin()), [&h2](const auto & val) { return (-*(std::next(&val,-1)) + 2.*val - *(std::next(&val)))/h2; }); // Final line of the matrix out[n-1] = (-in[n-2] + 2.*in[n-1])/h2; };
换句话说,for_each、transform这类并行算法是否会因某些执行策略破坏内存的连续性?
- 补充说明1:我不关心容器
in中元素的处理顺序,只在意lambda内通过*std::next(&val)能否正确获取in中的下一个元素,而非得到未定义的值。 - 补充说明2:我担心部分执行策略会将值复制到其他位置(如SIMD寄存器)后再执行lambda,请问C++标准中是否有相关条件阻止这种情况?
回答
你的写法确实会触发未定义行为,核心原因和正确的修正思路如下:
1. 原代码的根本问题
你在lambda中通过&val获取元素地址,再用指针偏移访问相邻元素的逻辑,完全依赖一个错误假设:val的地址就是原容器连续内存中的位置。但在par_unseq这类并行执行策略下,C++标准允许实现将元素复制到临时存储(比如SIMD寄存器、线程本地缓存)中处理,此时&val指向的是临时副本的地址,和原容器的内存布局没有任何连续关系。用这个地址去偏移访问,必然会得到错误值或触发内存访问错误。
2. C++标准的相关约束
标准并没有阻止并行算法复制元素到临时位置,反而允许这种优化来提升并行执行效率。同时,使用par_unseq时,用户提供的函数对象必须满足无依赖的独立元素处理——你的代码通过地址偏移访问其他元素,本质上是在元素处理中引入了未被标准允许的依赖关系,属于违规用法。
3. 正确的实现方式
必须放弃通过元素地址推导相邻元素的思路,改用索引或迭代器直接访问原容器中的元素:
方式一:基于索引的遍历(推荐)
auto A = [n,&h2](const auto & in, auto & out) { // 第一行 out[0] = (2.*in[0] - in[1])/h2 ; // 中间行:用索引直接访问原容器元素 std::for_each(std::execution::par_unseq, std::views::iota(1, n-1).begin(), std::views::iota(1, n-1).end(), [&in, &out, &h2](int idx) { out[idx] = (-in[idx-1] + 2.*in[idx] - in[idx+1])/h2; }); // 最后一行 out[n-1] = (-in[n-2] + 2.*in[n-1])/h2; };
方式二:基于迭代器的transform
如果坚持使用std::transform,可以传递迭代器作为lambda参数,直接通过迭代器访问相邻元素:
auto A = [n,&h2](const auto & in, auto & out) { // 第一行 out[0] = (2.*in[0] - in[1])/h2 ; // 中间行:迭代器作为参数 std::transform(std::execution::par_unseq, std::next(in.cbegin()), std::prev(in.cend()), std::next(out.begin()), [&h2](auto it) { return (-*(std::prev(it)) + 2.*(*it) - *(std::next(it)))/h2; }); // 最后一行 out[n-1] = (-in[n-2] + 2.*in[n-1])/h2; };
内容的提问来源于stack exchange,提问作者Astor
相关产品推荐
相关产品推荐

