我正在使用 OpenMP 并行化 for 循环。我正在尝试访问 C++ Armadillo vector by thread 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
vector 进行读/写?
最佳答案
您的代码没问题。重要的是要了解这一点
- 在并行区域外声明的变量隐式
共享
。 - 在并行区域内声明的变量是隐式
私有(private)
- 因此每个线程都有它的本地拷贝。
所以为每个线程声明一个私有(private)的距离 vector 并不是很有用。您甚至不必拥有单独的 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
意味着每个线程都得到一个本地拷贝,在并行区域之后,reduction 操作应用于本地拷贝集。这是最简洁、惯用和性能最佳的解决方案。
附言使用 C++ 代码,有时会有点难以判断访问是否是尽管 operator[]
或 arma::mat::row
在多线程程序中是安全的。您总是必须弄清楚您的代码是否意味着写入和/或读取共享数据。只有一个线程可以独占写入或许多线程可以读取。
关于c++ - OpenMP:通过线程 ID 访问共享变量时是否需要临界区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45513385/