OpenMP:当我按线程 ID 访问共享变量时,我是否需要一个关键部分



我正在使用OpenMP来并行化for循环。我正在尝试通过线程 id 访问C++犰狳矢量,但我想知道即使不同的线程访问不相交的内存区域,我是否必须将访问放在关键部分。 这是我的代码:

#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向量进行读/写?

你的代码很好。重要的是要了解

  • 并行区域之外声明的变量隐式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意味着每个线程获取一个本地副本,在并行区域之后,缩减操作将应用于本地副本集。这是最简洁、最惯用和性能最佳的解决方案。

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

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

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

最新更新