如何避免每次通过openmp for循环重新初始化向量?

问题描述

我有一个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中释放内存,因此我们不必每次都重新分配内存。

但是如果我想用openmp并行化for循环怎么办?我不能让所有线程都共享一个向量“ vec”。如此看来,我需要回到第一个选项,并在每次循环时都重新初始化向量,如下所示:

#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<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的次数也可能会很大。当对大量内存进行多次处理时,在向量中分配内存的速度很慢。这样做的目的是避免每次都要重新分配存储在vec中的动态分配内存,而不必进行重新分配。关心的不是矢量对象的堆栈分配的内存占用空间(它很小且分配迅速),而是属于矢量对象的堆分配的内存。

解决方法

实际上非常简单。 #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
    }
}

这将按您的预期工作。

这是一个明显的例子,其中为每个循环初始化向量的“干净”解决方案通常会引起性能损失。

有时,如果此“缓存”向量是全局/静态变量,则可能要使用#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,则会影响性能。这可能比“干净”的解决方案更糟糕。

如果由于某些原因必须从外部引入矢量。使用firstprivate创建私人副本,即:

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()会爆炸。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...