インテル® C++ コンパイラー XE 13.1 ユーザー・リファレンス・ガイド

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

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

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

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

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

signal 節と wait 節および offload_wait 構造は、特定のターゲットデバイスに関連するため、target() 節で target-number を指定する必要があります。

シグナルが開始される前にシグナルを照会すると、未定義の動作を引き起こし、アプリケーションはランタイムアボートします。例えば、ターゲットデバイス 1 で開始されたシグナル (SIG1) をターゲットデバイス 0 で照会する場合について考えてみます。シグナルはターゲットデバイス 1 で開始されているため、ターゲットデバイス 0 に関連付けられたシグナル (SIG1) はなく、アプリケーションはアボートします。

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

signal (tag) 節を使用している場合、if-specifier が FALSE と評価されると、シグナルは未定義となり、このシグナルの待機は未定義の動作を引き起こします。

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

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

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

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

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

次の例は、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 とコプロセッサー間の非同期データ転送 (INOUT) と非同期計算を行います。 配列 f1f2 のデータ転送は行 28 ~ 30 で開始されます。 offload_transfer では計算を開始しません。 f1f2 のデータをコプロセッサーへ転送するだけです。 行 40 ~ 44 で CPU は、コプロセッサーでの関数 add_inputs の計算開始を指示し、行 51 の offload_wait まで実行を継続します。 オフロード関数は、CPU からあらかじめ転送されたデータ f1f2 を使用します。 コプロセッサーのオフロード領域の実行は、f1f2 の転送が完了し、シグナル (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_transferf1f2 を別々にコプロセッサーに送信しています (行 63 ~ 64 で f1、行 68 ~ 69 で f2)。 これらの転送は独立しています。行 81 ~ 85 のコプロセッサーのオフロード領域と関数 add_inputs の実行は、f1f2 の転送が完了し、シグナル (f1f2) が受信された後に開始されます。 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) を行います。オフロード関数は、データ f1f2 を使用します。 f2 の転送は、あらかじめ行 97 ~ 98 で CPU により開始されています。 行 111 ~ 115 のコプロセッサーのオフロード領域の実行は、f1f2 の転送が完了し、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)

関連情報


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