OpenMP:当我通过线程标识

时间:2017-08-04 18:30:02

标签: c++ multithreading openmp armadillo

我正在使用OpenMP来并行化for循环。我试图通过线程ID访问C++ Armadillo向量,但我想知道是否必须将访问权限置于一个关键部分,即使不同的线程访问不相交的内存区域。 这是我的代码:

#include <armadillo>
#include <omp.h>
#include <iostream>

int main()
{

        arma::mat A = arma::randu<arma::mat>(1000,700);
        arma::rowvec point = A.row(0);
        arma::vec distances = arma::zeros(omp_get_max_threads());

        #pragma omp parallel shared(A,point,distances)
        {

                arma::vec local_distances = arma::zeros(omp_get_num_threads());
                int thread_id = omp_get_thread_num();

                for(unsigned int l = 0; l < A.n_rows; l++){
                        double temp = arma::norm(A.row(l) - point,2);
                        if(temp > local_distances[thread_id])
                                local_distances[thread_id] = temp;
                }

                // Is it necessary to put a critical section here?
                #pragma omp critical 
                if(local_distances[thread_id] > distances[thread_id]){
                        distances[thread_id] = local_distances[thread_id];
                }

        }

        std::cout << distances[distances.index_max()] << std::endl;

}

在我的情况下是否有必要对distances向量进行读/写操作?

2 个答案:

答案 0 :(得分:2)

多线程的难度来自于处理共享可变状态的需要。一个线程访问可变(可更改)数据或许多线程同时访问不可变(常量)数据没有任何问题。只有当多个线程需要访问相同的可变数据时才需要同步/关键部分。

您的代码属于第一种情况,因为每个thread_id都会索引到唯一数据 - 一次只有一个线程会更改数据。

答案 1 :(得分:2)

你的代码很好。了解

非常重要
  • 在并行区域之外声明的变量隐式private
  • 在并行区域内声明的变量是隐式local_distances - 因此每个线程都有一个本地副本。

因此,为每个线程声明一个私有的距离向量并不是很有用。您甚至不必拥有单独的distances,因为distances的访问权限是正确的。 (虽然应该注意,访问arma::mat A = arma::randu<arma::mat>(1000,700); arma::rowvec point = A.row(0); double distance = 0.; #pragma omp parallel reduction(max:distance) { for(unsigned int l = 0; l < A.n_rows; l++){ distance = std::max(distance, arma::norm(A.row(l) - point,2)); } } std::cout << distance << std::endl; 的效率非常低,因为不同的线程会尝试在同一个缓存行上写入数据)。无论如何,整个事情被称为减少,OpenMP很容易支持。您可以写如下:

reduction

声明变量operator[]意味着每个线程都获得本地副本,并且在并行区域之后,缩减操作将应用于本地副本集。这是最简洁,惯用和性能最佳的解决方案。

P.S。使用C ++代码,有时可能有点难以确定是否访问例如虽然arma::mat::row或{{1}}在多线程程序中是安全的。您总是需要弄清楚您的代码是否意味着从共享数据中写入和/或读取。只有一个线程可以只写许多线程可以读取。