【发布时间】:2022-01-18 06:08:58
【问题描述】:
我在以下函数中遇到分段错误,该函数使用向量插入与 OpenMP 并行创建点网格。
std::vector<n_point_t> fill_points(size_t Nn1, size_t Nn2) {
std::vector<n_point_t> grid;
grid.reserve(Nn1*Nn2);
#pragma omp parallel for
for (size_t i=0; i<Nn1; i++) {
std::vector<n_point_t> subgrid = get_subgrid(Nn2);
grid.insert(grid.begin()+i*Nn2, subgrid.begin(), subgrid.end());
}
return grid;
}
n_point_t 定义为
union n_point_t {
double coords[6];
struct {
double n1x;
double n1y;
double n1z;
double n2x;
double n2y;
double n2z;
};
};
而get_subgrid(size_t Nn2) 创建一个n_point_t 大小为Nn2 的网格。
插入肯定是造成分段错误的原因。我不明白这里的问题。由于插入索引,每个线程都应该插入grid 的不同部分。
即使我使用 #pragma omp critical 保护插入,我也会遇到分段错误。
【问题讨论】:
-
这是一个非常糟糕的主意。向量插入会修改向量的内容并使迭代器无效。当您执行
begin() + i * Nn2时,您还使用了可能已经结束的迭代器。为什么不预先分配向量并让您的函数就地填充值? -
我不是已经用
reserve()预先分配了吗?我是 C++ 新手,这大致就是我在 python 中使用 numpy 数组的方式。如果我为向量保留高达Nn1*Nn2,begin() + i * Nn2怎么会超过向量的末尾?我之所以这样写,是因为我希望get_subgrid()可供此代码的用户使用,如果他们想自己自定义构建网格。 -
你分配了 容量 但向量仍然包含零个元素,所以
begin() == end()。插入是将元素添加到向量中。尝试resize而不是reserve,然后就地复制数据而不是使用insert。此外,您确定(来自代码分析)对于这个简单的数组初始化,使用 OpenMP 会更快吗?在我看来,你做了很多不必要的分配,然后你也有线程同步的开销,这可能会被一个天真的单线程初始化程序打败,除非你的 subgrid 调用很昂贵。 -
我明白了!我认为
reserve()类似于np.empty()。我可以使用调整大小。老实说,我将其作为一个实践问题并行化,以解决使用 OpenMP 构建的任何问题,并练习我多年前学习的 OpenMP。我将在项目的其他点需要 OpenMP,但在这里并不是必需的。感谢您的帮助!