このトピックは、インテル® メニー・インテグレーテッド・コア (インテル® MIC) アーキテクチャーにのみ適用されます。
CPU とコプロセッサー間でデータを転送するには、すべて in 節またはすべて out 節の offload_transfer プラグマを使用します。 signal 節が指定されていない場合、データ転送は同期され、その次の文はデータ転送が完了した後に実行されます。
offload_transfer に signal 節が指定されると、データ転送は非同期になります。 signal 節で指定されるタグは、そのデータセットに関連付けられているアドレス式です。 データ転送が開始され、CPU は そのプラグマ以降の文を続行します。
コプロセッサーはタグに関連付けられているデータをすべて受信すると、後の wait 節が記述されたプラグマで指定されている処理を開始します。 データは、データ転送の開始時に指定された変数に格納されます。これらの変数はアクセス可能でなければなりません。
CPU からコプロセッサーにデータを非同期で転送するには、offload_transfer プラグマの in 節で signal 節を使用します。 in 節にリストされる変数によりデータセットが形成されます。 プラグマは、CPU からコプロセッサーへこれらの変数のデータ転送を開始します。 wait 節を含む以降の offload プラグマ (signal 節で使用されたタグと同じ値を使用) で制御された文は、データ転送が完了した後にコプロセッサーで実行されます。
非同期オフロード中に 1 つのスレッド (スレッド A) でシグナルが作成され、別のスレッドが (スレッド B) そのシグナルを待機する場合、スレッド A が非同期オフロードを開始しシグナルを設定する前にスレッド B がそのシグナルを照会しないようにしなければなりません。スレッド A が非同期オフロードを開始しシグナルを設定する前にスレッド B がそのシグナルを照会すると、未定義の動作を引き起こし、ランタイムでアプリケーションがアボートします。
次の例では、浮動小数点配列 f1 と f2 のデータ転送が 11 行目で開始されます。 オフロードは計算を開始しません。 f1 と f2 をコプロセッサーへ転送するだけです。 22 行目で、CPU はコプロセッサーで関数 foo の計算を開始します。 この関数は、直前に転送が開始されたデータ f1 と f2 を使用します。 コプロセッサーのオフロード領域の実行は、f1 と f2 の転送が完了した後に開始されます。 変数 result は、計算結果を返します。
01 const int N = 4086;
02 float *f1, *f2;
03 float result;
04 f1 = (float *)memalign(64, N*sizeof(float));
05 f2 = (float *)memalign(64, N*sizeof(float));
...
10 // CPU はデータを送信して続行
11 #pragma offload_transfer in( f1, f2 : length(N) ) signal(f1)
12
...
20 // CPU は計算を行うようにリクエスト
21 // コプロセッサーはリクエストを受信してあらかじめ送信されたデータを待つ
22 #pragma offload wait(f1) out( result )
23 {
24 result = foo(N, f1, f2);
25 }
複数の独立した非同期データ転送が発生する可能性があります。次の例では、offload_transfer プラグマを使用して、f1 と f2 を別々にコプロセッサーに送信しています (11 行目で f1、21 行目で f2)。
01 const int N = 4086;
02 float *f1, *f2;
03 float result;
04 f1 = (float *)memalign(64, N*sizeof(float));
05 f2 = (float *)memalign(64, N*sizeof(float));
...
10 // CPU は f1 を送信して続行
11 #pragma offload_transfer in( f1 : length(N) ) signal(f1)
12
...
20 // CPU は f2 を送信して続行
21 #pragma offload_transfer in( f2 : length(N) ) signal(f2)
22
...
30 // CPU は f1 と f2 を使用して計算を行うようにリクエスト
31 // コプロセッサーはあらかじめ送信されたデータを受信した後にのみ実行を開始
32 #pragma offload wait(f1, f2) out( result )
33 {
34 result = foo(N, f1, f2);
35 }
コプロセッサーから CPU にデータを非同期で転送するには、2 つの異なるプラグマ/宣言子で signal 節と wait 節を使用します。 最初のプラグマはデータ転送を開始し、次のプラグマはデータ転送が完了するのを待機します。
次の例では、浮動小数点配列 f1 と f2 のデータ転送が 11 行目で開始されます。 このプラグマは計算を開始しません。f1 と f2 をコプロセッサーへ転送するだけです。 22 行目で、CPU はコプロセッサーで関数 foo の計算を開始します。この関数は、直前に転送が開始されたデータ f1 と f2 を使用します。 コプロセッサーのオフロード領域の実行は、f1 と f2 の転送が完了した後に開始されます。 変数 result は、計算結果を返します。
01 const int N = 4086;
02 float *f1, *f2;
03 f1 = (float *)memalign(64, N*sizeof(float));
04 f2 = (float *)memalign(64, N*sizeof(float));
...
10 // CPU は入力として f1 を同期送信
11 // 出力は f2 にあるが直ちに必要ではない
12 #pragma offload in( f1 : length(N) ) \
13 nocopy( f2 : length(N) ) signal(f2)
14 {
15 foo(N, f1, f2);
16 }
..
20 #pragma offload_transfer wait(f2) out( f2 : length(N)
21
22 // CPU が f2 の結果を使用できるようになる
23 ...
次の例は、オフロードの入力をダブルバッファーにしています。
#pragma offload_attribute(push, target(mic))
int count = 25000000;
int iter = 10;
float *in1, *out1;
float *in2, *out2;
#pragma offload_attribute(pop)
void do_async_in()
{
int i;
#pragma offload_transfer target(mic:0) in(in1 : length(count) alloc_if(0) free_if(0) ) signal(in1)
for (i=0; i<iter; i++)
{
if (i%2 == 0) {
#pragma offload_transfer target(mic:0) if(i!=iter-1) in(in2 : length(count) alloc_if(0) free_if(0) ) signal(in2)
#pragma offload target(mic:0) nocopy(in1) wait(in1) out(out1 : length(count) alloc_if(0) free_if(0) )
compute(in1, out1);
} else {
#pragma offload_transfer target(mic:0) if(i!=iter-1) in(in1 : length(count) alloc_if(0) free_if(0) ) signal(in1)
#pragma offload target(mic:0) nocopy(in2) wait(in2) out(out2 : length(count) alloc_if(0) free_if(0) )
compute(in2, out2);
}
}
}