我需要一个 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
的哪个元素,我关心的是当我在*std::next(&val)
中的lambda内执行std::transform
时,我是否获得了下一个向量 in
中的元素,而不是未定义的元素。
编辑2:我正在考虑一些策略,这些策略意味着将值复制到其他地方(例如SIMD寄存器)并在返回结果之前在那里执行lambda。标准中的执行策略或并行算法是否存在阻止这种情况发生的条件?
不,这不安全 [algorithms.parallel.exec]p3:
除非另有说明,实现可以从
和T
为 true 的序列中任意复制元素(类型为is_trivially_copy_constructible_v<T>
)。is_trivially_destructible_v<T>
注释中给出的解决方案是包装迭代器:
auto ref_view = in | std::views::transform([](const auto& i) { return std::cref(i); });
std::transform(std::execution::par_unseq,
std::next(ref_view.begin()),std::prev(ref_view.end()),std::next(out.begin()),
[&h2](auto ref)
{
const auto& val = ref.get();
return (-*(std::next(&val,-1)) + 2.*val - *(std::next(&val)))/h2;
});