C++:使用 OpenMP 插入 std::vector

Posted

技术标签:

【中文标题】C++:使用 OpenMP 插入 std::vector【英文标题】:C++: std::vector insert with OpenMP 【发布时间】: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*Nn2begin() + i * Nn2 怎么会超过向量的末尾?我之所以这样写,是因为我希望 get_subgrid() 可供此代码的用户使用,如果他们想自己自定义构建网格。 你分配了 容量 但向量仍然包含零个元素,所以begin() == end()。插入是将元素添加到向量中。尝试resize 而不是reserve,然后就地复制数据而不是使用insert。此外,您确定(来自代码分析)对于这个简单的数组初始化,使用 OpenMP 会更快吗?在我看来,你做了很多不必要的分配,然后你也有线程同步的开销,这可能会被一个天真的单线程初始化程序打败,除非你的 subgrid 调用很昂贵。 我明白了!我认为reserve() 类似于np.empty()。我可以使用调整大小。老实说,我将其作为一个实践问题并行化,以解决使用 OpenMP 构建的任何问题,并练习我多年前学习的 OpenMP。我将在项目的其他点需要 OpenMP,但在这里并不是必需的。感谢您的帮助! 【参考方案1】:

由于您提前致电reserve(),因此这里不会发生重新分配。但是你将一个危险的论点grid.begin()+i*Nn2 传递给insert。不保证它是有效的迭代器。

如果subgrid的长度小于N2怎么办?你会得到一个不连续的向量吗?请不要这样做。它在单个线程中工作,只是因为 grid.begin()+i*Nn2 恰好是有效的。换句话说,不要试图去触碰向量中未使用的内存。

一个建议的解决方案可能是resize() 向量,如果您必须使用多个线程,请分配它们。

【讨论】:

以上是关于C++:使用 OpenMP 插入 std::vector的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 OpenMP 在 C++ 中实现监视器?

NodeJS:具有多线程的本机 C++ 模块(openmp)

在 C++ 中使用 OpenMP 并行化递归函数

在 C++ 中使用 OpenMP 并行化算法

预期的枚举`std::result::Result`,发现结构`std::vec::Vec`。

C++:OpenMP 中的私有静态变量