2017-04-11 15 views
1

検索スペースをn個に分割するn回検索のコードを記述しています。 並列コードとOpenMPディレクティブがないコード、すなわちシリアル実行を比較すると、並列コードはシリアルコードよりも何度も遅いことがわかりました。両方のプログラムを何度も実行した後は、並列コードでスピードアップはほとんど見られませんでしたが、毎回ではありませんでした。これはおそらくキャッシュの階層構造によるものです。私は4GBのRAMを搭載したクアッドコアプロセッサでプログラムを実行しています。OpenMPを使用したn回検索の高速化なし

答えによるとNo speedup with OpenMPメモリバインドされたパフォーマンスとロードバランシングは、配列SIZE 100などの小さな問題には適用されません。私は同期を使用していません。配列サイズを10000000に増やしてみましたが、パラレルコードの出力が必ずしも速くはありません。多くの場合、シリアルコードはパラレルコードよりも優れています。

によるhttp://pages.tacc.utexas.edu/~eijkhout/pcse/html/omp-loop.htmlワークシェアリングコンストラクトの最後の暗黙のバリアは、nowait句で取り消すことができます。私もnowait節を追加しようとしましたが、私もhttps://software.intel.com/en-us/articles/openmp-loop-schedulingを参照してスケジュール(動的)とスケジュール(自動)を試みましたが、それでも同じ問題がありました。

コード:

#include <stdio.h> 
#include <stdlib.h> 
#include <omp.h> 

#define SIZE 100 
#define NUM_THREADS 4 

int* a; 
int num; 


void nary(int num) 
{ 
    int found = 0, low = 0, high = SIZE, step; 
    int i = 0; 
    while(!found && low <= high) 
    { 
     step = (high-low)/NUM_THREADS; 
     printf("Low :- %d\tHigh :- %d\tStep :- %d\n", low,high,step); 
     printf("\n"); 

     #pragma omp parallel for num_threads(NUM_THREADS) shared(low,high,step) 
     for (i = 0; i < NUM_THREADS; ++i) 
     { 
      printf("First element :- %d by thread :- %d\n", a[low+step*i],omp_get_thread_num()); 
      if (a[low+step*i] == num) 
      { 
       found = 1; 
      } 
     } 

     printf("\n"); 
     /* First block */ 
     if (a[low+step] > num) 
     { 
      high = low + step - 1; 
      printf("First \nLow :- %d \nHigh :- %d\n\n",low,high); 
     } 

     /* Last block */ 
     else if (a[low+step*(NUM_THREADS-1)] < num) 
     { 
      low = low + step * (NUM_THREADS-1) + 1; 
      printf("Last\nLow :- %d \nHigh :- %d\n\n",low,high); 
     } 
     /* Middle blocks */ 
     else{ 
      #pragma omp parallel for num_threads(NUM_THREADS) schedule(static) shared(low,high,step) 
      for (i = 1; i < (NUM_THREADS-1); ++i) 
      { 
       if (a[low+step*i] < num && a[low+step*(i+1)] > num) 
       { 
        low = low + step*i + 1; 
        high = low + step*(i+1) - 1; 
       } 
      } 
      printf("middle\nLow :- %d \nHigh :- %d\n\n",low,high); 
     } 
    } 
    if (found == 1) 
    { 
     printf("Element found\n"); 
    } 
    else 
    { 
     printf("Element Not found\n"); 
    } 

} 

int main() 
{ 
    int i = 0; 
    int startTime = omp_get_wtime(); 

    /* Dynamically allocate memory using malloc() */ 
    a = (int*)malloc(sizeof(int) * SIZE); 
    #pragma omp parallel for schedule(static) 
    for (i = 0; i < SIZE; ++i) 
    { 
     a[i] = i; 

    } 

    printf("Enter the element to be searched :- \n"); 
    scanf("%d", &num); 

    nary(num); 


    printf("\nExecution time :- %f\n", omp_get_wtime()-startTime); 
    return 0; 
} 

並列実行出力:

Enter the element to be searched :- 
20 
Low :- 0 High :- 100 Step :- 25 

First element :- 0 by thread :- 0 
First element :- 50 by thread :- 2 
First element :- 25 by thread :- 1 
First element :- 75 by thread :- 3 

First 
Low :- 0 
High :- 24 

Low :- 0 High :- 24 Step :- 6 

First element :- 6 by thread :- 1 
First element :- 18 by thread :- 3 
First element :- 0 by thread :- 0 
First element :- 12 by thread :- 2 

Last 
Low :- 19 
High :- 24 

Low :- 19 High :- 24 Step :- 1 

First element :- 20 by thread :- 1 
First element :- 21 by thread :- 2 
First element :- 19 by thread :- 0 
First element :- 22 by thread :- 3 

middle 
Low :- 19 
High :- 24 

Element found 

Execution time :- 26.824379 

シリアル実行出力:

Enter the element to be searched :- 
20 
Low :- 0 High :- 100 Step :- 25 

First element :- 0 by thread :- 0 
First element :- 25 by thread :- 0 
First element :- 50 by thread :- 0 
First element :- 75 by thread :- 0 

First 
Low :- 0 
High :- 24 

Low :- 0 High :- 24 Step :- 6 

First element :- 0 by thread :- 0 
First element :- 6 by thread :- 0 
First element :- 12 by thread :- 0 
First element :- 18 by thread :- 0 

Last 
Low :- 19 
High :- 24 

Low :- 19 High :- 24 Step :- 1 

First element :- 19 by thread :- 0 
First element :- 20 by thread :- 0 
First element :- 21 by thread :- 0 
First element :- 22 by thread :- 0 

middle 
Low :- 19 
High :- 24 

Element found 

Execution time :- 4.349347 

これの背後にある理由は何か?これは、条件付きブロック内のコードまたはforループに多くの条件文があるためですか?

+1

新しいスレッドを開始するには時間がかかります。並行して実行される作業の量は、それを得るのに十分な大きさでなければなりません。 –

+1

コードは完全にCのようですが、C++にタグを付けることを決めた理由は何ですか? – Zulan

+0

@BoPersson配列サイズ10000で試しましたが、同じ結果を返しました – daemon7osh

答えて

2

あなたのアプローチには多くの大きな問題があります。

まず第一に、バイナリ検索は非常に高速です。それは最悪の場合、ログ(n)回の反復しか必要としない。 1兆個の要素が検索されても、それは40回の反復だけです!各反復は非常に単純であり、基本的に単一のメモリアクセスのみが必要です。だから我々は、大規模なデータセットの最悪の場合、数マイクロ秒の検索時間について話している。それはもちろん、printfとのものを汚染することなくです。

一方、スレッドを生成するには、someanswersに従って約10マイクロ秒かかります。したがって、完璧なスケーリングの実装であっても、単一のの検索を並列化することに基づくパフォーマンスの改善のための現実的なチャンスはありません。

特定のコードを見ると、反復ごとに2つの並列領域が作成されます。各スレッドは、並列領域のオーバーヘッドや、ワークシェアリング構成(実装とOSによって大きく異なる場合がある)と比べて、わずかな作業量しかありません。

私はアリティとの混合が問題であることを発見しました。更新手順は2回のシリアル実行で構成され、残りのNUM_THREADS-2間隔はNUM_THREADSスレッドによってチェックされます。NUM_THREADS=4の場合、完全な並列実行であっても、4回のインターバルチェックから3回のインターバルチェックまでの時間が短縮され、1.3倍のスピードアップ更新ステップで。

さらに、コードに重大な競合条件が含まれています。lowに基づいて他のスレッドが同時に間隔をチェックしているため、2番目の並列ループでlowを変更することは非常に悪い考えです。

ソートされた連続データの検索のパフォーマンスを実質的に向上させる場合は、these slidesをチェックしてください。 OpenMP /スレッドを使用してアプリケーションを高速化したい場合は、おそらくこれをより粗いレベルで行うべきです。

+0

提案したスライドの概念を理解できません。競合状態は 'shared(low、high、step)'を使用して回避することができます。コードの変更を提案できますか? – daemon7osh

+1

'shared'宣言は何も変更しません。変数は並列領域外で宣言されているため、暗黙的に共有されます。実際には、共有変数だけが競合条件を持つことができます。私は、OpenMPの基本がSOの答えの範囲を超えていることを説明するのは怖いです。答えに書いたように、スレッドベースの並列化による単一の検索のパフォーマンスを改善しようとするのは無駄ですから、コードの変更を提案することはできません。 – Zulan

関連する問題