1

hereから取得した以下のコードを検討してください。このコードの実行時間は次のとおりです。

time ./fibomp 40
Number of threads (OpenMP v200805): 2
finonacci(40) = 102334155

real    0m3.193s
user    0m3.180s
sys     0m0.000s

$ export OMP_NUM_THREADS=1
$ time ./fibomp 40
Number of threads (OpenMP v200805): 1
finonacci(40) = 102334155

real    0m3.224s
user    0m3.216s
sys     0m0.000s

ご覧のとおり、スピードアップはあまりありません。Ruud が 2011 年 11 月 1 日火曜日午前 1 時 41 分にメールで言及した 2 倍のスピードアップとはまったく違います。これをデュアルコアマシンで実行しています(それでしょうか?)。私は何を間違っていますか?(ところで、ボーナスポイント、ptimeコマンドは何ですか?SPARC Unixコマンドですか?)

long comp_fib_numbers(int n)
{
  long fnm1, fnm2, fn;
  if ( n == 0 || n == 1 ) return(n);

  // In case the sequence gets too short, execute the serial version
  if ( n < 20 )
  {
     return(comp_fib_numbers(n-1)+comp_fib_numbers(n-2));
  }
  else
  {
     #pragma omp task shared(fnm1)
       fnm1 = comp_fib_numbers(n-1);
     #pragma omp task shared(fnm2)
       fnm2 = comp_fib_numbers(n-2);
     #pragma omp taskwait
       fn = fnm1 + fnm2;
       return(fn);
   }

}
4

1 に答える 1

4

まず、念のためhtop、単一のコアが使用されていることを示しているため、コンパイラで OpenMP サポートが有効になっていることを確認してください。これを行うオプションは-fopenmp、GCC、-xopenmpSun/Oracle コンパイラ、および-openmpIntel コンパイラ用です。

第二にn = 20、並列実装のカットオフが低すぎる可能性があります。恥知らずなプラグ -数か月前に私の同僚が行った OpenMP に関するワークショップのこのコース資料を参照してください。そこでは、スライド 20 から始めて、タスキングを使用したいくつかの並列バージョンについて説明しています。

3 つ目ptimeは Solaris コマンドであり、x86 バージョンでも使用できるため、SPARC に固有のものではありません。多くのプロセス関連の Solaris コマンドにはp、名前にプレフィックスが含まれています。timeあなたの場合、スタンドアロンのバイナリではなく、Bash が提供する組み込みの実装である可能性が高いことに注意してください。

4番目に、あなたの質問に対する本当の答えかもしれませんparallel.コードに領域が欠けているため、タスクディレクティブがまったく機能しません:)コードを次のように書き直す必要があります。

long comp_fib_numbers(int n)
{
   long fnm1, fnm2, fn;
   if ( n == 0 || n == 1 ) return(n);

   // In case the sequence gets too short, execute the serial version
   if ( n < 20 )
   {
      return(comp_fib_numbers(n-1)+comp_fib_numbers(n-2));
   }
   else
   {
      #pragma omp parallel  // <--- You are missing this one parallel region
      {
         #pragma omp single
         {
            #pragma omp task shared(fnm1)
            fnm1 = comp_fib_numbers(n-1);
            #pragma omp task shared(fnm2)
            fnm2 = comp_fib_numbers(n-2);
         }
         #pragma omp taskwait
      }

      fn = fnm1 + fnm2;
      return(fn);
   }

}

if句を使用して並列領域を切り替えることで、コードをさらに簡潔にすることができます。

long comp_fib_numbers(int n)
{
   long fnm1, fnm2, fn;
   if ( n == 0 || n == 1 ) return(n);

   #pragma omp parallel if(n >= 20)
   {
      #pragma omp single
      {
         #pragma omp task shared(fnm1)
         fnm1 = comp_fib_numbers(n-1);
         #pragma omp task shared(fnm2)
         fnm2 = comp_fib_numbers(n-2);
      }
      #pragma omp taskwait
   }

   fn = fnm1 + fnm2;
   return(fn);
}

nたまたま 20 未満の場合、並列領域はシングル スレッドで実行されます。並列領域は通常、個別の関数で抽出されるため、コンパイラが重複コードの生成を選択しない限り、追加の関数呼び出しが依然として存在します。そのため、シリアル実装を独自の関数で抽出することをお勧めします。

long comp_fib_numbers_serial(int n)
{
   if ( n == 0 || n == 1 ) return(n);

   return (comp_fib_numbers_serial(n-1) + comp_fib_numbers_serial(n-2));
}

long comp_fib_numbers(int n)
{
   long fnm1, fnm2, fn;
   if ( n < 20 ) return comp_fib_numbers_serial(n);

   #pragma omp parallel
   {
      #pragma omp single
      {
         #pragma omp task shared(fnm1)
         fnm1 = comp_fib_numbers(n-1);
         #pragma omp task shared(fnm2)
         fnm2 = comp_fib_numbers(n-2);
      }
      #pragma omp taskwait
   }

   fn = fnm1 + fnm2;
   return(fn);
}

編集:リンク先のコードを見たので、への呼び出しがリージョンcomp_fib_numbersに埋め込まれていることがわかりparallelます。parallelしたがって、コードに既にリージョンがある場合は、欠落しているリージョンに関する私のコメントを無視してください。完全を期すためにここに残しておきます。パラレル バージョンとシリアル バージョンの間の切り替えが発生する値を微調整してみてください。最新のプロセッサではかなり高くなる可能性があり、あなたが見た例はかなり古いものです。また、環境変数OMP_DYNAMICを にfalse(または に) 設定するか、並列領域の前の場所FALSEを呼び出して、動的チームが使用されないようにしてください。omp_set_dynamic(0);

コンパイラが何であるかは述べていませんが、OpenMP 3.0 はバージョン 4.4 以降の GCC、バージョン 11.0 以降の Intel コンパイラ、バージョン I_dont_know 以降の Sun/Oracle コンパイラでサポートされており、Visual C/C++ コンパイラではまったくサポートされていません。 .

クアッドソケット Intel Xeon X7350 システム (FSB を使用した古い Nehalem 以前のシステム) で観測された速度向上

$ time OMP_NUM_THREADS=1 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=1 ./fib.x 40  1.86s user 0.00s system 99% cpu 1.866 total
$ time OMP_NUM_THREADS=2 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=2 ./fib.x 40  1.96s user 0.00s system 169% cpu 1.161 total

カットオフが設定されて25いる場合 (X7350 の最適な値のようです):

$ time OMP_NUM_THREADS=2 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=2 ./fib.x 40  1.95s user 0.00s system 169% cpu 1.153 total

カットオフを設定し25、シリアル実装用の別の関数を使用すると、次のようになります。

$ time OMP_NUM_THREADS=2 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=2 ./fib.x 40  1.52s user 0.00s system 171% cpu 0.889 total

ユーザー時間が約 400 ミリ秒減少する様子を確認してください。これは、オーバーヘッドが削除されたためです。

これらは、リンク先のサイトのコードを使用して測定されました。使用されるコンパイラは、64 ビット Scientific Linux 6.2 システム上の GCC 4.4.6 です。

于 2012-10-16T16:27:14.343 に答える