for 循环内的多线程 - OpenMP

Multi threading inside a for loop - OpenMP

本文关键字:OpenMP 多线程 循环 for      更新时间:2023-10-16

我正在尝试在C++代码中添加多线程。目标是函数内部的 for 循环。目标是减少程序的执行时间。执行需要 3.83 秒。

我试图在内部循环中添加命令#pragma omp parallel for reduction(+:sum)在 j for 循环之前),但这还不够。花了1.98秒。目的是将时间减少到 0.5 秒。

我做了一些研究来提高速度,有些人推荐带状挖掘方法进行矢量化以获得更好的结果。但是我还不知道如何实现它。

有人知道该怎么做吗?

代码为:

void filter(const long n, const long m, float *data, const float threshold, std::vector &result_row_ind) {
  for (long i = 0; i < n; i++) {
    float sum = 0.0f;
    for (long j = 0; j < m; j++) {
      sum += data[i*m + j];
    } 
    if (sum > threshold) 
      result_row_ind.push_back(i);
  }
  std::sort(result_row_ind.begin(),
            result_row_ind.end());
}

谢谢

如果可能,您可能希望并行化外部循环。 在 OpenMP 中执行此操作的最简单方法是执行以下操作:

#pragma omp parallel for
for (long i = 0; i < n; i++) {
  float sum = 0.0f;
  for (long j = 0; j < m; j++) {
    sum += data[i*m + j];
  }
  if (sum > threshold) {
    #pragma omp critical
    result_row_ind.push_back(i);
  }
}
std::sort(result_row_ind.begin(),
          result_row_ind.end());

这有效,并且可能比并行化内部循环快得多(启动并行区域很昂贵),但它使用关键部分进行锁定以防止竞争。也可以通过在向量上使用用户定义的缩减来避免竞争,如果线程数非常大并且匹配结果的数量非常少,这可能会更慢,但否则可能会明显更快。 这不太正确,矢量类型不完整,因为它没有列出,但应该非常接近:

#pragma omp declare 
  reduction(CatVec: std::vector<T>: 
    omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end())) 
  initializer(omp_priv=std::vector<T>())
#pragma omp parallel for reduction(CatVec: result_row_ind)
for (long i = 0; i < n; i++) {
  float sum = 0.0f;
  for (long j = 0; j < m; j++) {
    sum += data[i*m + j];
  }
  if (sum > threshold) {
    result_row_ind.push_back(i);
  }
}
std::sort(result_row_ind.begin(),
          result_row_ind.end());
如果您有支持

执行策略的C++编译器,则可以尝试使用执行策略std::execution::par std::for_each,看看是否有帮助。例:

#include <iostream>
#include <vector>
#include <algorithm>
#if __has_include(<execution>)
# include <execution>
#elif __has_include(<experimental/execution_policy>)
# include <experimental/execution_policy>
#endif
// iterator to use with std::for_each
class iterator {
    size_t val;
public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = size_t;
    using difference_type = size_t;
    using pointer = size_t*;
    using reference = size_t&;
    iterator(size_t value=0) : val(value) {}
    inline iterator& operator++() { ++val; return *this; }
    inline bool operator!=(const iterator& rhs) const { return val != rhs.val; }
    inline reference operator*() { return val; }
};
std::vector<size_t> filter(const size_t rows, const size_t cols, const float* data, const float threshold) {
    std::vector<size_t> result_row_ind;
    std::vector<float> sums(rows);
    iterator begin(0);
    iterator end(rows);
    std::for_each(std::execution::par, begin, end, [&](const size_t& row) {
        const float* dataend = data + (row+1) * cols;
        float& sum = sums[row];
        for (const float* dataptr = data + row * cols; dataptr < dataend; ++dataptr) {
            sum += *dataptr;
        }
    });
    // pushing moved outside the threaded code to avoid using mutexes
    for (size_t row = 0; row < rows; ++row) {
        if (sums[row] > threshold)
            result_row_ind.push_back(row);
    }
    std::sort(result_row_ind.begin(),
        result_row_ind.end());
    return result_row_ind;
}
int main() {
    constexpr size_t rows =  1<<15, cols = 1<<18;
    float* data = new float[rows*cols];
    for (int i = 0; i < rows*cols; ++i) data[i] = (float)i / (float)100000000.;
    std::vector<size_t> res = filter(rows, cols, data, 10.);
    std::cout << res.size() << "n";
    delete[] data;
}