非同期データ転送について

このトピックは、インテル® メニー・インテグレーテッド・コア (インテル® MIC) アーキテクチャーにのみ適用されます。

CPU とコプロセッサー間でデータを転送するには、すべて in 節またはすべて out 節の offload_transfer プラグマを使用します。 signal 節が指定されていない場合、データ転送は同期され、その次の文はデータ転送が完了した後に実行されます。

offload_transfersignal 節が指定されると、データ転送は非同期になります。 signal 節で指定されるタグは、そのデータセットに関連付けられているアドレス式です。 データ転送が開始され、CPU は そのプラグマ以降の文を続行します。

コプロセッサーはタグに関連付けられているデータをすべて受信すると、後の wait 節が記述されたプラグマで指定されている処理を開始します。 データは、データ転送の開始時に指定された変数に格納されます。これらの変数はアクセス可能でなければなりません。

CPU からコプロセッサーへの非同期データ転送

CPU からコプロセッサーにデータを非同期で転送するには、offload_transfer プラグマin 節で signal 節を使用します。 in 節にリストされる変数によりデータセットが形成されます。 プラグマは、CPU からコプロセッサーへこれらの変数のデータ転送を開始します。 wait 節を含む以降の offload プラグマ (signal 節で使用されたタグと同じ値を使用) で制御された文は、データ転送が完了した後にコプロセッサーで実行されます。

非同期オフロード中に 1 つのスレッド (スレッド A) でシグナルが作成され、別のスレッドが (スレッド B) そのシグナルを待機する場合、スレッド A が非同期オフロードを開始しシグナルを設定する前にスレッド B がそのシグナルを照会しないようにしなければなりません。スレッド A が非同期オフロードを開始しシグナルを設定する前にスレッド B がそのシグナルを照会すると、未定義の動作を引き起こし、ランタイムでアプリケーションがアボートします。

例: CPU からコプロセッサー

次の例では、浮動小数点配列 f1f2 のデータ転送が 11 行目で開始されます。 オフロードは計算を開始しません。 f1f2 をコプロセッサーへ転送するだけです。 22 行目で、CPU はコプロセッサーで関数 foo の計算を開始します。 この関数は、直前に転送が開始されたデータ f1f2 を使用します。 コプロセッサーのオフロード領域の実行は、f1f2 の転送が完了した後に開始されます。 変数 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 プラグマを使用して、f1f2 を別々にコプロセッサーに送信しています (11 行目で f121 行目で 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 への非同期データ転送

コプロセッサーから CPU にデータを非同期で転送するには、2 つの異なるプラグマ/宣言子で signal 節と wait 節を使用します。 最初のプラグマはデータ転送を開始し、次のプラグマはデータ転送が完了するのを待機します。

例: コプロセッサーから CPU

次の例では、浮動小数点配列 f1f2 のデータ転送が 11 行目で開始されます。 このプラグマは計算を開始しません。f1f2 をコプロセッサーへ転送するだけです。 22 行目で、CPU はコプロセッサーで関数 foo の計算を開始します。この関数は、直前に転送が開始されたデータ f1f2 を使用します。 コプロセッサーのオフロード領域の実行は、f1f2 の転送が完了した後に開始されます。 変数 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);
            }
      }
}

このヘルプトピックについてのフィードバックを送信