我正在使用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
向量进行读/写操作?
答案 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}}在多线程程序中是安全的。您总是需要弄清楚您的代码是否意味着从共享数据中写入和/或读取。只有一个线程可以只写或许多线程可以读取。