2016-07-28 4 views
2

私はOpenMPを使って、ほぼ線形のスピードアップを持つアルゴリズムを手に入れようとしています。 残念ながら、私は希望のスピードアップを得ることができなかったことに気付きました。OpenMPでスピードアップしない

私のコードのエラーを理解するために、簡単に別のコードを書いて、原理的に高速化がハードウェア上で可能であることを再確認しました。

これは私が書いたおもちゃの例である:

#include <omp.h> 
#include <cmath> 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include <string.h> 
#include <cstdlib> 
#include <fstream> 
#include <sstream> 
#include <iomanip> 
#include <iostream> 
#include <stdexcept> 
#include <algorithm> 
#include "mkl.h" 

int main() { 
     int number_of_threads = 1; 
     int n = 600; 
     int m = 50; 
     int N = n/number_of_threads; 
     int time_limit = 600; 
     double total_clock = omp_get_wtime(); 
     int time_flag = 0; 

     #pragma omp parallel num_threads(number_of_threads) 
     { 
      int thread_id = omp_get_thread_num(); 
      int iteration_number_local = 0; 
      double *C = new double[n]; std::fill(C, C+n, 3.0); 
      double *D = new double[n]; std::fill(D, D+n, 3.0); 
      double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 

      while (time_flag == 0){ 
       for (int i = 0; i < N; i++)      
        for(int z = 0; z < m; z++) 
         for(int x = 0; x < n; x++) 
          for(int c = 0; c < n; c++){ 
           CD[c] = C[z]*D[x]; 
           C[z] = CD[c] + D[x]; 
          } 
       iteration_number_local++; 
       if ((omp_get_wtime() - total_clock) >= time_limit) 
        time_flag = 1; 
      } 
     #pragma omp critical 
     std::cout<<"I am "<<thread_id<<" and I got" <<iteration_number_local<<"iterations."<<std::endl; 
     } 
    } 

私はこのコードが高速化を確認しようとするだけのおもちゃ-例であることを改めて強調表示したい:とき数サイクルの最初は短くなり(Nが減少するので)増加する。

しかし、私が1から2-4のスレッドに行くと、期待どおりに反復回数が倍増します。しかし、これは私が8-10-20のスレッドを使用する場合には当てはまりません。反復回数はスレッドの数に比例して増加しません。

私にこれを手伝ってもらえますか?コードは正しいですか?私は、ほぼ直線的なスピードアップを期待する必要がありますか?私は以下の結果を得た上でコードを実行する

結果

スレッド1回:23回繰り返します。

20スレッド:スレッドごとに397-401反復(420-460ではなく)。

+0

実行中のハードウェアは?プロセッサーとメモリーについて具体的に記述してください。どのようなコンパイラのバージョンとオプションとどのオペレーティングシステムですか?いくつの反復を観察していますか? – Zulan

+0

測定に問題があります。「CD」は決して使用されないので、コンパイラはあなたが高価であると予想しているものすべてを最適化できます。少なくとも 'iteration_number_local'をすべて出力するべきです(' pragma omp critical'を使います)。 – Zulan

+0

私は2つの10コアIntel Xeon-E5(したがって、合計20コア)と256GBのRAMを搭載したハードウェア上でコードを実行しています。 オペレーティングシステムはLinuxです。 私はコンパイラについて知らない: "gsl 1.15"というモジュールをロードし、cmakeは "icc"というコンパイラを呼び出します。私はこれがあなたが尋ねたものではないと思います、私にはっきりしてください。 n = 1000、m = 200でいくつかの高速シミュレーションを実行します。 1スレッドでは、120秒で3回の繰り返しが得られます。 2つのスレッドでは、スレッドごとに5回の繰り返しを行います(6ではなく)。 20スレッドのスレッドでは、スレッドごとに40と44回の繰り返しが行われます(60ではなく!)。 – Mobius88

答えて

0

並列領域内にいくつかの宣言を行います。これは、メモ領域を割り当ててnumber_of_threads回埋め込むことを意味します。代わりに私はあなたにお勧めします:

double *C = new double[n]; std::fill(C, C+n, 3.0); 
double *D = new double[n]; std::fill(D, D+n, 3.0); 
double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 
#pragma omp parallel firstprivate(C,D,CD) num_threads(number_of_threads) 
    { 
     int thread_id = omp_get_thread_num(); 
     int iteration_number_local = 0; 
    } 

ハードウェアにはプロセッサのコア数に応じて限られた数のスレッドしかありません。あなたは2つまたは4つのコアを持つかもしれません。

並列領域はコードを高速化しません。 OpenMPを開いてループを高速化するには#omp parallelを使用するか、

#pragma omp parallel 
{ 
    #pragma omp for 
    { 
    } 
} 

この表記は#pragma omp parallel forと同じです。 forループをより速く進めるために、いくつかのスレッドを使用します(ハードウェアに依存します)。気をつけて

#pragma omp parallel 
{ 
    for 
    { 
    } 
} 

は、あなたのプログラムをスピードアップしません、各スレッドのためのforループ全体を行います。

+0

私はあなたが正しいとは確信していません。 #pragma omp parallel ブロック内のすべてのコマンドを個別に実行するために必要なスレッド数を許可します。 したがって、各スレッドは並列ブロック内でネストされたforを実行します。 スレッドの数が増えると最初のサイクルが短くなるので、線形のスピードアップが必要です。 このコードは、ノードあたり20コアのクラスタコンピュータで実行しています。 – Mobius88

+0

もちろん最初のサイクルは短くなりますが、number_of_threads回行うことになります。最後に、n/number_of_threads * number_of_threadsの操作を行います。 –

+0

の#pragma ompの並列 { の#pragma ompのは { } ため} { { } ため } 並列 と の#pragma ompのたぶん私は何かが欠けています同じ命令 –

0

あなたは

#pragma omp parallel num_threads(number_of_threads) 
    { 
     int thread_id = omp_get_thread_num(); 
     int iteration_number_local = 0; 
     double *C = new double[n]; std::fill(C, C+n, 3.0); 
     double *D = new double[n]; std::fill(D, D+n, 3.0); 
     double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 

     while (time_flag == 0){ 
      #pragma omp for 
      for (int i = 0; i < N; i++)      
       for(int z = 0; z < m; z++) 
        for(int x = 0; x < n; x++) 
         for(int c = 0; c < n; c++) 
          CD[c] = C[z]*D[x]; 
      iteration_number_local++; 
      if ((omp_get_wtime() - total_clock) >= time_limit) 
       time_flag = 1; 
     } 
     if(thread_id == 0) 
     iteration_number = iteration_number_local; 
    } 
    std::cout<<"Iterations= "<<iteration_number<<std::endl; 
} 
+2

新しい質問を投稿するのではなく、以前の回答を編集する必要があります。 –

1

あなたの測定方法が間違って試してみてください。特に反復回数が少ない場合。

1スレッド:3回繰り返します。

3は、実際に2反復未満120秒で終了することを意味反復を報告しました。 3番目の方が長くかかりました。 1回の反復の時間は40〜60秒です。

2スレッド:スレッドごとに5回の繰り返し(6回ではなく)。

4回の反復が120秒以内に終了しました。 1反復の時間は24〜30秒です。

20スレッド:40-44スレッドごとの反復(60ではなく)。

40回の反復が120秒以内に終了しました。 1回の反復の時間は2.9〜3秒である。

あなたの結果が実際に直線的なスピードアップに矛盾しないことがわかります。

単純に1つの外側ループを実行して時間を計るだけで、ほぼ完璧なリニアなスピードアップが実現します。

いくつかの理由(非網羅)あなたは、線形スピードアップが表示されない理由は以下のとおりです。

  1. メモリーバインドのパフォーマンス。あなたのおもちゃの例では、n = 1000ではありません。より一般的な話題:共有リソース(メインメモリ、キャッシュ、I/O)の競合。
  2. スレッド間の同期(クリティカルセクションなど)。あなたのおもちゃの例ではそうではありません。
  3. スレッド間のアンバランスをロードします。あなたのおもちゃの例ではそうではありません。
  4. ターボモードは、すべてのコアが使用されているときに低い周波数を使用します。あなたのおもちゃの例でこれが起こります。

あなたのおもちゃの例からは、OpenMPのアプローチは、高水準の抽象化を使用して改善することができます。 for

一般的なアドバイスは、このフォーマットでは広すぎるため、非おもちゃの例に関するより具体的な情報が必要です。

+0

私はあなたの答えに同意するので、私はより長いシミュレーションを実行します。 入れ子ループ内に別の命令を含めることによって、上記のようなコードを実行します。C [z] = CD [c] + D [x];あなたが示唆したように、CDを使用するために。 n = 600とm = 50を設定すると、600秒間に1スレッド、600秒間に20スレッドで1スレッドあたり400回の反復で23 iterが得られます。予想されるスピードアップではありません。私は正しい? – Mobius88

+0

399/22は20倍のスピードアップにかなり近いです。実際のアプリケーションではほぼ直線的なスピードアップとして完全に受け入れられるほど十分に近いまた、ターボモードやばらつきだけでも簡単に説明できます。 – Zulan

+0

配列はL1キャッシュに収まるほど小さいので、おそらく周波数スケーリングです。 –

関連する問題