インテル® C++ コンパイラー 17.0 デベロッパー・ガイドおよびリファレンス
このトピックは、インテル® メニー・インテグレーテッド・コア (インテル® MIC) アーキテクチャーにのみ適用されます。
CPU とコプロセッサー間でデータを転送するには、すべて in 節またはすべて out 節の offload_transfer プラグマを使用します。 signal 節が指定されていない場合、データ転送は同期され、それ以降の文はデータ転送が完了した後に実行されます。
offload_transfer に signal 節が指定されると、データ転送は非同期になります。 signal 節で指定されるタグは、そのデータセットに関連付けられているアドレス式です。 データ転送が開始され、CPU はそのプラグマ以降の文を続行します。
コプロセッサーはタグに関連付けられているデータをすべて受け取ると、後の wait 節が記述されたプラグマで指定されている処理を開始します。
データは、データ転送の開始時に指定された変数に格納されます。これらの変数はアクセス可能でなければなりません。
または、非ブロック API _Offload_signaled() を使用して、特定のターゲットデバイスでオフロードコードのセクションの実行が完了したかどうか判断することもできます。
signal 節と wait 節、offload_wait 構造、および _Offload_signaled() API は、特定のターゲットデバイスに関連するため、target() 節で target-number を指定する必要があります。
シグナルが開始される前にシグナルを照会すると、未定義の動作を引き起こし、アプリケーションはランタイムアボートします。 例えば、ターゲットデバイス 1 で開始されたシグナル (SIG1) をターゲットデバイス 0 で照会する場合について考えてみます。 シグナルはターゲットデバイス 1 で開始されているため、ターゲットデバイス 0 に関連付けられたシグナル (SIG1) はなく、アプリケーションはアボートします。
非同期オフロード中に 1 つのスレッド (スレッド A) でシグナルが作成され、別のスレッドが (スレッド B) そのシグナルを待機する場合、スレッド A が非同期オフロードを開始しシグナルを設定する前はスレッド B がそのシグナルを照会してはなりません。これを行うと、アプリケーションはランタイムアボートします。
signal (tag) 節を使用している場合、if-specifier が FALSE と評価されると、シグナルは未定義となり、このシグナルの待機は未定義の動作を引き起こします。
CPU からコプロセッサーにデータを非同期で転送するには、offload_transfer プラグマの in 節で signal 節を使用します。
in 節にリストされる変数によりデータセットが形成されます。
プラグマは、CPU からコプロセッサーへこれらの変数のデータ転送を開始します。
wait 節を含む以降の offload プラグマ (signal 節で使用された tag と同じ値を使用) で制御された文は、データ転送が完了した後にコプロセッサーで実行されます。
コプロセッサーから CPU にデータを非同期で転送するには、2 つの異なるプラグマで signal 節と wait 節を使用します。 最初のプラグマはデータ転送を開始し、次のプラグマはデータ転送が完了するのを待機します。
次の例は、CPU とコプロセッサー間のいくつかの非同期データ転送の例を示します。
1 #include <stdio.h> 2 3 __attribute__((target(mic))) 4 void add_inputs(int N, float *f1, float*f2); 5 6 void display_vals(int id, int N, float*f2); 7 8 int main() 9 { 10 const int N = 5; 11 float *f1, *f2; 12 int i, j; 13 14 f1 = (float *)_mm_malloc(N*sizeof(float),4096); 15 f2 = (float *)_mm_malloc(N*sizeof(float),4096); 16 17 for (i=0;i<N;i++){ 18 f1[i]=i+1; 19 f2[i]=0.0; 20 } 21
以下の "セクション 1" (行 22 ~ 56) は、CPU とコプロセッサー間の非同期データ転送 (IN と OUT) と非同期計算を行います。 配列 f1 と f2 のデータ転送は行 28 ~ 30 で開始されます。 offload_transfer では計算を開始しません。 f1 と f2 のデータをコプロセッサーへ転送するだけです。 行 40 ~ 44 で CPU は、コプロセッサーでの関数 add_inputs の計算開始を指示し、行 51 の offload_wait まで実行を継続します。 オフロード関数は、CPU からあらかじめ転送されたデータ f1 と f2 を使用します。 コプロセッサーのオフロード領域の実行は、f1 と f2 の転送が完了し、シグナル (f1) が設定された後に開始されます。 コプロセッサーでオフロード領域を実行している間、CPU は行 51 でオフロード計算が完了し、f2 の結果が CPU に転送されるのを待ちます。 f2 のデータが CPU に転送され、シグナル (f2) を受信した後にのみ、CPU で行 51 以降の実行が継続されます。
22 //----------- セクション 1 -------------------------------------- 23 24 // f1 と f2 のコプロセッサーへの非同期転送 (IN) 25 // 26 // CPU はデータを送信して続行 27 28 #pragma offload_transfer target(mic:0) signal (f1) \ 29 in( f1 : length(N) alloc_if(1) free_if(0) ) \ 30 in( f2 : length(N) alloc_if(1) free_if(0) ) 31 32 // 非同期計算と f2 の CPU への非同期転送 (OUT) 33 // 34 // CPU は計算の実行をリクエストして続行 35 // 36 // コプロセッサーはリクエストを受信してあらかじめ送信されたデータを待つ 37 // データを受け取った後に計算を実行し、38 // 結果を CPU へ非同期転送 (OUT) 39 40 #pragma offload target(mic:0) wait(f1) signal (f2) \ 41 in( N ) \ 42 nocopy( f1 : alloc_if(0) free_if(1) ) \ 43 out( f2 : length(N) alloc_if(0) free_if(1) ) 44 add_inputs(N, f1, f2); 45 46 // オフロード計算の完了を待つ 47 // 48 // CPU はオフロード計算と 49 // f2 の CPU へのデータ転送の完了を待つ 50 51 #pragma offload_wait target(mic:0) wait(f2) 52 53 54 // 現在の値を表示 55 display_vals(1, N, f2); 56
このプログラムの "セクション 2" (行 57 ~ 90) は、CPU からコプロセッサーへの非同期データ転送 (IN) と同期計算、そしてコプロセッサーから CPU への非同期転送 (OUT) を含む複数の非同期データ転送を行います。 複数の独立した非同期データ転送が発生する可能性があります。offload_transfer は f1 と f2 を別々にコプロセッサーに送信しています (行 63 ~ 64 で f1、行 68 ~ 69 で f2)。 これらの転送は独立しています。行 81 ~ 85 のコプロセッサーのオフロード領域と関数 add_inputs の実行は、f1 と f2 の転送が完了し、シグナル (f1 と f2) が受信された後に開始されます。 CPU は、オフロード計算が完了し、f2 の結果が CPU へ転送されるまで実行を待機します。 f2 の CPU へのデータ転送は、オフロード領域の実行と同期的に行われます。
57 //----------- セクション 2 -------------------------------------- 58 59 // 独立したコプロセッサーへの非同期転送 (IN) 60 // 61 // CPU はデータを送信して続行 62 63 #pragma offload_transfer target(mic:0) signal (f1) \ 64 in( f1 : length(N) alloc_if(1) free_if(0) ) 65 66 // CPU はデータを送信して続行 67 68 #pragma offload_transfer target(mic:0) signal (f2) \ 69 in( f2 : length(N) alloc_if(1) free_if(0) ) 70 71 // 独立したコプロセッサーへの転送 (IN) を待つ 72 // 同期計算とデータ転送 (OUT) を行う 73 // 74 // CPU は計算の実行をリクエストして 75 // その完了を待つ 76 // 77 // コプロセッサーはリクエストを受信してあらかじめ送信されたデータを待つ 78 // データを受け取った後に計算を実行し、79 // 結果を CPU へ同期転送 (OUT) 80 81 #pragma offload target(mic:0) wait(f1 , f2) \ 82 in( N ) \ 83 nocopy( f1 : alloc_if(0) free_if(1) ) \ 84 out( f2 : length(N) alloc_if(0) free_if(1) ) 85 add_inputs(N, f1, f2); 86 87 88 // 現在の値を表示 89 display_vals(2, N, f2); 90
このプログラムの "セクション 3" (行 91 ~ 132) は、独立した CPU からコプロセッサーへの非同期データ転送 (IN)、CPU からコプロセッサーへの同期データ転送 (IN) と計算、独立したコプロセッサーから CPU への非同期データ転送 (OUT) を行います。オフロード関数は、データ f1 と f2 を使用します。 f2 の転送は、あらかじめ行 97 ~ 98 で CPU により開始されています。 行 111 ~ 115 のコプロセッサーのオフロード領域の実行は、f1 と f2 の転送が完了し、f2 の転送のシグナル (f2) が受信された後に開始されます。 コプロセッサーでオフロード領域を実行した後、f2 の結果はコプロセッサーで保持され、CPU は行 116 以降の実行を継続できます。 行 122 ~ 123 で、CPU は f2 の計算結果をコプロセッサーから CPU へ非同期データ転送 (OUT) し、行 128 まで実行を継続して、f2 の転送が完了するのを待ちます。 f2 のデータが CPU に転送され、シグナル (f2) が受信された後でのみ、CPU で行 128 以降の実行が継続されます。
91 //----------- セクション 3 -------------------------------------- 92 93 // f2 のコプロセッサーへの非同期転送 (IN) 94 // 95 // CPU はデータを送信して続行 96 97 #pragma offload_transfer target(mic:0) signal(f2) \ 98 in( f2 : length(N) alloc_if(1) free_if(0) ) 99 100 // f1 のコプロセッサーへの非同期転送 (IN) と 101 // f2 の同期計算。計算結果 f2 は 102 // コプロセッサーで保持 103 // 104 // CPU は f1 をコプロセッサーへ転送 (IN) し、105 // 計算の実行をリクエストしてその完了を待つ 106 // 107 // コプロセッサーはリクエストを受信してあらかじめ送信された 108 // f2 のデータを待つ; データを受け取った後に計算を実行し、109 // 結果はコプロセッサーの f2 に保持 110 111 #pragma offload target(mic:0) wait(f2) \ 112 in( N ) \ 113 in ( f1 : length(N) alloc_if(1) free_if(0) ) \ 114 nocopy( f2 ) 115 add_inputs(N, f1, f2); 116 117 118 // CPU はオフロード計算の完了を待ち、119 // f2 の CPU への非同期転送 (OUT) を開始して 120 // 続行 121 122 #pragma offload_transfer target(mic:0) signal (f2) \ 123 out( f2 : length(N) alloc_if(0) free_if(1) ) 124 125 126 // CPU は f2 の CPU への転送の完了を待つ 127 128 #pragma offload_wait target(mic:0) wait(f2) 129 130 // 現在の値を表示 131 display_vals(3, N, f2); 132 133 } 134 135 void add_inputs (int N, float *f1, float*f2) 136 { 137 int i; 138 139 for (i=0; i<N; i++){ 140 f2[i] = f2[i] + f1[i]; 141 } 142 } 143 144 void display_vals (int id, int N, float *f2) 145 { 146 int i; 147 148 printf("\nResults after Offload #%d:\n",id); 149 for (i=0; i<N; i++){ 150 printf(" f2[%d]= %f\n",i,f2[i]); 151 } 152 }
次の例は、オフロードの入力をダブルバッファーにしています。
#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); } } }