2017-08-04 11 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

マルチスレッドの難しさは、共有可能な可変状態に対処する必要があるからです。変更可能な(変更可能な)データにアクセスする1つのスレッド、または不変の(定数)データに同時にアクセスする多くのスレッドには何も問題はありません。同期/クリティカルセクションが必要な同じ可変データに複数のスレッドがアクセスする必要があるのは、それだけです。

各スレッドIDが一意のデータにインデックスを付けるため、コードは最初のケースに該当します。一度に1つのスレッドだけがデータを変更します。

2

コードは問題ありません。

  • パラレル領域外で宣言された変数は、暗黙的にsharedであることを理解することが重要です。
  • 並列領域内で宣言された変数は暗黙的にprivateです。各スレッドにはローカルコピーがあります。

したがって、スレッドごとに距離のプライベートベクトルを宣言することは実際には有用ではありません。 distancesへのアクセスが正しいので、別途local_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は、マルチスレッドプログラムでは安全です。あなたのコードが共有データからの書き込みおよび/または読み込みを意味するかどうかを常に把握しなければなりません。 1つのスレッドだけがまたはの多くのスレッドを読み込み専用に書き込みます。

+0

ありがとう!実際には、最大距離を持つ 'A 'の行のインデックスを取得できるようにコードを修正する必要がありました。だから、私は最大距離だけでなく、その行のインデックスも必要とします。質問を更新するか、別の投稿を作成する必要がありますか?しかし、私は、削減変数がそのようなものを処理できるかどうかはわかりません。 – Cauchy

+0

max reductionとlastprivateの組み合わせ(Cではなく、C++)がよく働くことが多いが、ユーザー定義リダクションは通常max要素に対して主張されている。 – tim18

+0

OpenMPユーザー定義の削減でargmaxを実行する方法については、[この回答](https://stackoverflow.com/a/42771196/620382)をご覧ください。 – Zulan

関連する問題