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