最近のGCCバージョンで解決されたように見えるGCCベクトル化装置には欠点があります。首尾よく次のような単純なループ私のテストケースGCC 4.7.2 vectorisesで:
同じ時間のGCC 4.6.1でないと、それはループを分析することができない関数呼び出しやデータの参照が含まれていることを、文句を言います。ベクトル化のバグは、parallel for
のループがGCCによって実装される方法によって引き起こされます。OpenMPの構築物が処理され、展開されている場合は、単純なループコードは、これに似た何かに変換される:4.7前
struct omp_fn_0_s
{
int N;
double *a;
double *b;
double *c;
double d;
};
void omp_fn_0(struct omp_fn_0_s *data)
{
int start, end;
int nthreads = omp_get_num_threads();
int threadid = omp_get_thread_num();
// This is just to illustrate the case - GCC uses a bit different formulas
start = (data->N * threadid)/nthreads;
end = (data->N * (threadid+1))/nthreads;
for (int i = start; i < end; i++)
data->a[i] = data->b[i] + data->c[i] * data->d;
}
...
struct omp_fn_0_s omp_data_o;
omp_data_o.N = N;
omp_data_o.a = a;
omp_data_o.b = b;
omp_data_o.c = c;
omp_data_o.d = d;
GOMP_parallel_start(omp_fn_0, &omp_data_o, 0);
omp_fn_0(&omp_data_o);
GOMP_parallel_end();
N = omp_data_o.N;
a = omp_data_o.a;
b = omp_data_o.b;
c = omp_data_o.c;
d = omp_data_o.d;
GCCでvectoriserは、そのループをvectoriseに失敗しました。これはOpenMP固有の問題ではありません。 OpenMPコードなしで簡単に再現できます。 - なぜならエイリアシングが起こらないことを指定するために使用restrict
キーワードでも等しくvectoriseすべき
struct fun_s
{
double *restrict a;
double *restrict b;
double *restrict c;
double d;
int n;
};
void fun1(double *restrict a,
double *restrict b,
double *restrict c,
double d,
int n)
{
int i;
for (i = 0; i < n; i++)
a[i] = b[i] + c[i] * d;
}
void fun2(struct fun_s *par)
{
int i;
for (i = 0; i < par->n; i++)
par->a[i] = par->b[i] + par->c[i] * par->d;
}
一つは、両方のコード(!ここにはOpenMPの通知が)ことを期待する。このことを確認するために、私は、次の簡単なテストを書きました。残念ながら、これはGCC < 4.7では当てはまりません。fun1
でループをベクトル化しますが、fun2
ではOpenMPコードをコンパイルするときと同じ理由を挙げてベクトル化できません。
この理由はvectoriserがpar->a
、par->b
、およびpar->c
ポイントはにことをpar->d
がメモリ内にないことを証明することができないということです。これは、常に2つのケースが考えられるfun1
、の場合ではありません。
d
は、レジスタの値を引数として渡されます。
d
は、スタック上で値の引数として渡されます。
x64システムでは、最初のいくつかの浮動小数点引数がXMMレジスタ(AVX対応CPUのYMM)に渡されることが必須です。つまり、d
がこの場合に渡され、ポインタがポインタを指すことはできません。ループがベクトル化されます。 x86システムでは、ABIは引数がスタックに渡されるように指示します。したがって、d
は3つのポインタのいずれかによってエイリアスされることがあります。実際、GCCは、-m32
オプションを持つ32ビットx86コードを生成するよう指示された場合には、fun1
のループをベクトル化することを拒否します。
GCC 4.7では、実行時チェックを挿入することにより、d
とpar->d
のどちらにもエイリアスが発生しないようにしています。 d
退治
はunprovable非エイリアシングと次のOpenMPコードはGCC 4.6.1でベクトル化されます削除:
#pragma omp parallel for schedule(static)
for (int i = 0; i < N; i++)
a[i] = b[i] + c[i];
私はあなたがこの質問に[回答](http://stackoverflow.com/a/14717689/771663)で賢明な情報を見つけることができると思います。 – Massimiliano
OpenMPでSIMDを使用する方法を説明していただきありがとうございますが、OpenMPを使用するとSIMDの実装がうまく動作しない理由を説明していないようです。両方を使用する方法はありませんか? – superbriggs
これはまた、私は同じ数のビットでしか動作できないことを意味し、それらは数の間でちょうど分かれています。 GCCでそれをやっている間、私はレジスタに分割したいと思った人の数を尋ねられませんでした。私は大学の「スーパーコンピュータ」を使っているので、ハードウェアにはSIMD用のスペースが余分にあると仮定していました。それが正しいかどうかわかるでしょうか? – superbriggs