【问题标题】:OpenMP: Do I need a critical section when I access shared variable by thread idOpenMP:当我通过线程 ID 访问共享变量时,我是否需要一个关键部分
【发布时间】:2018-01-12 18:44:31
【问题描述】:

我正在使用 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 向量进行读/写操作?

【问题讨论】:

  • 不,这不应该需要关键部分。

标签: c++ multithreading openmp armadillo


【解决方案1】:

您的代码很好。了解这一点很重要

  • 在并行区域之外声明的变量隐式为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 在多线程程序中是安全的。您总是必须弄清楚您的代码是否意味着写入和/或读取共享数据。只有一个线程可以独占写入多个线程可以读取。

【讨论】:

  • 谢谢!实际上,我必须修改我的代码才能获得A 中具有最大距离的行的索引。所以我不仅需要最大距离,还需要该行的索引。我应该更新我的问题还是再发一个帖子?但我不确定减少变量是否可以处理类似的事情。
  • 用户定义的归约通常被提倡用于最大元素,尽管最大归约和 lastprivate(使用 C,而不是 C++)的组合通常效果更好。
  • 看看this answer,看看如何使用 OpenMP 用户定义的缩减来做 argmax。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-31
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    • 2020-12-28
    • 2015-01-11
    • 2014-08-07
    • 2011-10-30
    相关资源
    最近更新 更多