c++ - 如何避免每次通过 openmp for 循环重新初始化 vector ?

标签 c++ openmp hpc

我有一个如下所示的 for 循环:

for (int i = 0;i<N;i++) {
    vector<double> vec;
    //then do work on vec, such as resize or push_back
}

这是低效的,因为每次通过循环时,都会调整 vec 的大小,并且这可能会在每次通过 for 循环时强制进行动态内存分配。所以一个简单的优化是:

vector<double> vec;
for (int i = 0;i<N;i++) {
    vec.clear();
    //then do work on vec, such as resize or push_back
}

这更快,因为clear不会释放vec中的内存,因此我们不必每次都释放和重新分配内存。

但是如果我想将 for 循环与 openmp 并行化怎么办?我不能让所有线程共享一个 vector “vec”。所以看来我需要返回到第一个选项并每次通过循环重新初始化 vector ,如下所示:

#pragma omp parallel for
for (int i = 0;i<N;i++) {
    vector<double> vec;
    //then do work on vec, such as resize or push_back
}

有没有办法避免这种低效率并避免每次都重新分配 vector ?做这样的事情安全吗?

vector<vector<double>> outervec;
outervec.resize(omp_get_max_threads());

#pragma omp parallel for shared(outervec)
for (int i = 0;i<N;i++) {
    int tid = omp_get_thread_num();
    vector<double> &vec = outervec[tid];
    vec.clear();
    //then do work on vec, such as resize or push_back
}

当 vec 调整大小时,它可能会变得相当大,并且 for 循环 N 的次数也可能会很大。当对大块内存进行多次分配时,在 vector 中分配内存会很慢。这个想法是尽量避免每次都必须释放然后重新分配存储在 vec 中的动态分配内存。关心的不是 vector 对象的堆栈分配内存占用(它很小且分配很快),而是属于 vector 对象的堆分配内存。

最佳答案

其实很简单。 #pragma omp parallel for 是一个复合语句 - 您可以将其拆分:

#pragma omp parallel
{
    std::vector<double> vec;
    #pragma omp for
    for (int i = 0;i<N;i++) {
        vec.clear();
        //then do work on vec, such as resize or push_back
    }
}

这将按照您的预期正常工作。

这是一个明显的例子,其中为每个循环初始化 vector 的“干净”解决方案会带来性能损失,而这通常是相关的。

有时,如果此“缓存” vector 是全局/静态变量,您可能需要使用#pragma omp threadprivate指令。

您建议的解决方案:

std::vector<std::vector<double>> outervec;
outervec.resize(omp_get_max_threads());

#pragma omp parallel for shared(outervec)
for (int i = 0;i<N;i++) {
    int tid = omp_get_thread_num();
    auto& vec = outervec[tid];
    vec.clear();
    //then do work on vec, such as resize or push_back
}

可以工作,但还有另一个巨大的性能问题。这很可能会引入错误共享 - 多个 std::vector 实例使用的指针存储在同一缓存行中。如果频繁修改这些内容,即使用 push_back,性能将会受到影响。这很容易比“干净”的解决方案更糟糕。

如果出于某种原因必须从外部引入 vector 。使用 firstprivate 制作私有(private)拷贝,即:

std::vector<double> vec;
#pragma omp parallel for firstprivate(vec)
for (int i = 0;i<N;i++) {
    vec.clear();
    //then do work on vec, such as resize or push_back
}

不要使用private(vec),因为它会使变量未初始化,并且vec.clear()会爆炸。

关于c++ - 如何避免每次通过 openmp for 循环重新初始化 vector ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64489529/

相关文章:

c++ - fstream 文件 ("arr.txt") 在 Ubuntu14.04 上引发错误 "corrupted double-linked list, Aborted"但在 Windows 7 上运行良好

C++ OpenMP : Split for loop in even chunks static and join data at the end

parallel-processing - OpenMP - Fortran 中的任务依赖性

cluster-computing - Slurm:最大 SLURM_ARRAY_TASK_ID 的变量

c++ - 在模板 SFINAE 约束中使用间接级别会导致硬错误

c++ - 当第二个线程从 map 中删除值时如何恢复线程?

c++ - 交换后 vector 会保持连续吗?

c++ - 如何并行化循环的矩阵排序?

r - 如何配置批处理脚本以使用 future.batchtools (SLURM) 并行化 R 脚本

c - MPI_Isend 和 MPI_Irecv 似乎导致死锁