この回答は理由を十分に説明していると思いますが、ここで少し拡張します。
ただし、以前は、次のgcc 4.8 のドキュメントが-fopenmp
あります。
-fopenmp
C/C++ では OpenMP ディレクティブ #pragma omp および Fortran では !$omp の処理を有効にします。-fopenmp を指定すると、コンパイラは OpenMP Application Program Interface v3.0 http://www.openmp.org/に従って並列コードを生成します。このオプションは -pthread を意味するため、-pthread をサポートするターゲットでのみサポートされます。
機能の無効化を指定していないことに注意してください。実際、gcc が最適化を無効にする理由はありません。
ただし、1 スレッドの openmp に openmp がない場合と比べてオーバーヘッドがある理由は、コンパイラがコードを変換し、関数を追加して、n>1 スレッドの openmp の場合に備えられるようにする必要があるためです。それでは、簡単な例を考えてみましょう。
int *b = ...
int *c = ...
int a = 0;
#omp parallel for reduction(+:a)
for (i = 0; i < 100; ++i)
a += b[i] + c[i];
このコードは、次のように変換する必要があります。
struct __omp_func1_data
{
int start;
int end;
int *b;
int *c;
int a;
};
void *__omp_func1(void *data)
{
struct __omp_func1_data *d = data;
int i;
d->a = 0;
for (i = d->start; i < d->end; ++i)
d->a += d->b[i] + d->c[i];
return NULL;
}
...
for (t = 1; t < nthreads; ++t)
/* create_thread with __omp_func1 function */
/* for master thread, don't create a thread */
struct master_data md = {
.start = /*...*/,
.end = /*...*/
.b = b,
.c = c
};
__omp_func1(&md);
a += md.a;
for (t = 1; t < nthreads; ++t)
{
/* join with thread */
/* add thread_data->a to a */
}
これを で実行するとnthreads==1
、コードは実質的に次のように縮小されます。
struct __omp_func1_data
{
int start;
int end;
int *b;
int *c;
int a;
};
void *__omp_func1(void *data)
{
struct __omp_func1_data *d = data;
int i;
d->a = 0;
for (i = d->start; i < d->end; ++i)
d->a += d->b[i] + d->c[i];
return NULL;
}
...
struct master_data md = {
.start = 0,
.end = 100
.b = b,
.c = c
};
__omp_func1(&md);
a += md.a;
では、openmp なしのバージョンとシングルスレッドの openmp バージョンの違いは何ですか?
1 つの違いは、追加のグルー コードがあることです。openmp によって作成された関数に渡す必要がある変数は、1 つの引数を形成するためにまとめる必要があります。そのため、関数呼び出しの準備 (および後でデータを取得する) のオーバーヘッドが発生します。
しかし、もっと重要なことは、コードがもはや一体ではないということです。機能間の最適化はまだそれほど進んでおらず、ほとんどの最適化は各機能内で行われています。関数が小さいということは、最適化する可能性が小さいことを意味します。
-fopenmp
この回答を締めくくるために、 が のオプションにどのように影響するかを正確に示したいと思いgcc
ます。(注:私は現在古いコンピューターを使用しているため、gcc 4.4.3 を使用しています)
実行gcc -Q -v some_file.c
すると、次の(関連する)出力が得られます。
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=128106
options passed: -v a.c -D_FORTIFY_SOURCE=2 -mtune=generic -march=i486
-fstack-protector
options enabled: -falign-loops -fargument-alias -fauto-inc-dec
-fbranch-count-reg -fcommon -fdwarf2-cfi-asm -fearly-inlining
-feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fident
-finline-functions-called-once -fira-share-save-slots
-fira-share-spill-slots -fivopts -fkeep-static-consts -fleading-underscore
-fmath-errno -fmerge-debug-strings -fmove-loop-invariants
-fpcc-struct-return -fpeephole -fsched-interblock -fsched-spec
-fsched-stalled-insns-dep -fsigned-zeros -fsplit-ivs-in-unroller
-fstack-protector -ftrapping-math -ftree-cselim -ftree-loop-im
-ftree-loop-ivcanon -ftree-loop-optimize -ftree-parallelize-loops=
-ftree-reassoc -ftree-scev-cprop -ftree-switch-conversion
-ftree-vect-loop-version -funit-at-a-time -fvar-tracking -fvect-cost-model
-fzero-initialized-in-bss -m32 -m80387 -m96bit-long-double
-maccumulate-outgoing-args -malign-stringops -mfancy-math-387
-mfp-ret-in-387 -mfused-madd -mglibc -mieee-fp -mno-red-zone -mno-sse4
-mpush-args -msahf -mtls-direct-seg-refs
実行すると、次のgcc -Q -v -fopenmp some_file.c
(関連する)出力が得られます。
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=128106
options passed: -v -D_REENTRANT a.c -D_FORTIFY_SOURCE=2 -mtune=generic
-march=i486 -fopenmp -fstack-protector
options enabled: -falign-loops -fargument-alias -fauto-inc-dec
-fbranch-count-reg -fcommon -fdwarf2-cfi-asm -fearly-inlining
-feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fident
-finline-functions-called-once -fira-share-save-slots
-fira-share-spill-slots -fivopts -fkeep-static-consts -fleading-underscore
-fmath-errno -fmerge-debug-strings -fmove-loop-invariants
-fpcc-struct-return -fpeephole -fsched-interblock -fsched-spec
-fsched-stalled-insns-dep -fsigned-zeros -fsplit-ivs-in-unroller
-fstack-protector -ftrapping-math -ftree-cselim -ftree-loop-im
-ftree-loop-ivcanon -ftree-loop-optimize -ftree-parallelize-loops=
-ftree-reassoc -ftree-scev-cprop -ftree-switch-conversion
-ftree-vect-loop-version -funit-at-a-time -fvar-tracking -fvect-cost-model
-fzero-initialized-in-bss -m32 -m80387 -m96bit-long-double
-maccumulate-outgoing-args -malign-stringops -mfancy-math-387
-mfp-ret-in-387 -mfused-madd -mglibc -mieee-fp -mno-red-zone -mno-sse4
-mpush-args -msahf -mtls-direct-seg-refs
diff を取ると、唯一の違いは、-fopenmp
が-D_REENTRANT
定義されていること (そしてもちろん-fopenmp
有効になっていること) であることがわかります。したがって、gcc が悪いコードを生成することはありませんのでご安心ください。スレッド数が 1 よりも大きく、オーバーヘッドが発生する場合の準備コードを追加する必要があるだけです。
更新:最適化を有効にしてこれをテストする必要がありました。とにかく、gcc 4.7.3 では、同じコマンドの出力を追加-O3
すると、同じ違いが得られます。したがって、 を使用しても、-O3
最適化が無効になることはありません。