2017-08-04 92 views
2

我正在使用OpenMP来并行化for循环。我试图通过线程ID访问C++ Armadillo向量,但是我想知道是否必须将访问放在关键部分,即使不同线程访问不相交区域的内存。 这是我的代码:OpenMP:当我通过线程ID访问共享变量时是否需要临界区域

#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载体?

+1

不,这不应该要求关键部分。 – subzero

回答

2

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

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

2

你的代码没问题。理解这一点很重要

  • 声明在并行区域之外的变量隐含地为shared
  • 在并行区域内声明的变量隐含地为private - 因此每个线程都有其本地副本。

因此,为每个线程声明距离的私有向量并不是很有用。您甚至不必单独使用local_distances,因为访问distances是正确的。 (虽然应该注意,对distances的访问效率非常低,因为不同的线程会尝试在同一个缓存行上写入数据)。无论如何,整个事情被称为减少,OpenMP对此有轻松的支持。您可以编写类似如下:

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; 

声明一个变量reduction意味着每个线程都有一个本地副本,并在并行区域后,减少操作应用到集本地副本。这是最简洁,惯用和性能最佳的解决方案。

P.S.使用C++代码,有时可能难以确定是否访问例如尽管operator[]arma::mat::row在多线程程序中是安全的。您始终必须弄清楚您的代码是否意味着写入和/或从共享数据读取。只有一个线程可能独占地编写许多线程可能会读取。

+0

谢谢!实际上我不得不修改我的代码,以便能够获得具有最大距离的'A'中的行的索引。所以我不仅需要最大距离,而且还需要该行的索引。我应该更新我的问题还是发表另一篇文章?但我不确定减少变量是否可以处理类似的事情。 – Cauchy

+0

尽管最大缩减和lastprivate(使用C,而不是C++)组合通常效果更好,但用户定义的缩减通常主张max元素。 – tim18

+0

看看[这个答案](https://stackoverflow.com/a/42771196/620382),看看如何用OpenMP用户定义的缩减来完成argmax。 – Zulan

相关问题