OpenMP 与向量的向量并行

OpenMP parallel for with vector of vectors

本文关键字:向量 并行 OpenMP      更新时间:2023-10-16

>我有一个大小为W x H的固定大小的二维矩阵,矩阵中的每个元素都是一个std::vector。数据存储在具有线性索引的向量中。我正在尝试找到一种同时填充输出向量的方法。这里有一些代码来指示我正在尝试做什么。

#include <cmath>
#include <chrono>
#include <iostream>
#include <mutex>
#include <vector>
#include <omp.h>
struct Vector2d
{
    double x;
    double y;
};
double generate(double range_min, double range_max)
{
    double val = (double)rand() / RAND_MAX;
    return range_min + val * (range_max - range_min);
}
int main(int argc, char** argv)
{
    (void)argc;
    (void)argv;
    // generate input data
    std::vector<Vector2d> points;
    size_t num = 10000000;
    size_t w = 100;
    size_t h = 100;
    for (size_t i = 0; i < num; ++i)
    {
        Vector2d point;
        point.x = generate(0, w);
        point.y = generate(0, h);
        points.push_back(point);
    }
    // output
    std::vector<std::vector<Vector2d> > output(num, std::vector<Vector2d>());
    std::mutex mutex;
    auto start = std::chrono::system_clock::now();
    #pragma omp parallel for
    for (size_t i = 0; i < num; ++i)
    {
        const Vector2d point = points[i];
        size_t x = std::floor(point.x);
        size_t y = std::floor(point.y);
        size_t id = y * w + x;
        mutex.lock();
        output[id].push_back(point);
        mutex.unlock();
    }
    auto end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end - start;
    std::cout << "elapsed time: " << elapsed_seconds.count() << "sn";
    return 0;
}

问题是启用 openmp 后代码要慢得多。我找到了一些使用缩减填充 std::vector 的例子,但我不知道如何使其适应向量的向量。任何帮助是感激的,谢谢!

您可以采取一些措施来提高性能:

我会预分配保存Vector2d类的第二个向量,因为每次您push_backVector2d并且超出std::vector的容量时,它都会重新分配。因此,如果您不在乎在std::vector中初始化Vector2d,我将简单地使用:

std::vector<std::vector<Vector2d> > output(num, 
               std::vector<Vector2d>(num, Vector2d(/*whatever goes in here*/)));

然后在你的 for 循环中,你可以通过 operator[] 访问第二个向量中的元素,这允许你摆脱锁。

#pragma omp parallel for
for (size_t i = 0; i < num; ++i)
{
    const Vector2d point = points[i];
    size_t x = std::floor(point(0));
    size_t y = std::floor(point(1));
    size_t id = y * w + x;
    output[id][i] = num;
}

虽然我不确定,但前面提到的方法适用于您想要做的事情。 否则,您可以为每个std::vector<Vector2d>保留存储,这将为您留下初始循环:

std::vector<std::vector<Vector2d> > output(num, std::vector<Vector2d>());
for(int i = 0; i < num; ++i) {
    output[i].reserve(num);
}
#pragma omp parallel for
for (size_t i = 0; i < num; ++i)
{
    const Vector2d point = points[i];
    size_t x = std::floor(point(0));
    size_t y = std::floor(point(1));
    size_t id = y * w + x;
    mutex.lock();
    output[id].push_back(point);
    mutex.unlock();
}

这意味着你摆脱了向量重新分配,但你仍然有互斥锁......