インテル® Fortran コンパイラー 19.0 デベロッパー・ガイドおよびリファレンス
このトピックは、インテル® メニー・インテグレーテッド・コア (インテル® MIC) アーキテクチャーをターゲットとする場合にのみ適用されます。
CPU とターゲットデバイス間でデータを転送するには、すべて in 節またはすべて out 節の OFFLOAD_TRANSFER ディレクティブを使用します。signal 節が指定されていない場合、データ転送は同期され、それ以降の文はデータ転送が完了した後に実行されます。
OFFLOAD_TRANSFER に signal 節が指定されると、データ転送は非同期になります。signal 節で指定されるタグは、そのデータセットに関連付けられているアドレス式です。データ転送が開始され、CPU はそのディレクティブ以降の文を続行します。
ターゲットデバイスは、タグに関連付けられているすべてのデータの受信または共有が完了すると、後の wait 節が記述されたディレクティブで指定されている処理を開始します。データは、データ転送の開始時に指定された変数に格納されます。これらの変数はアクセス可能でなければなりません。
または、非ブロック API OFFLOAD_SIGNALED() を使用して、特定のターゲットデバイスでオフロードコードのセクションの実行が完了したかどうか判断することもできます。
インテル® AVX MIC アーキテクチャー上では、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 節を使用します。最初のディレクティブはデータ転送を開始し、次のディレクティブはデータ転送が完了するのを待機します。
次の例では、浮動小数点配列 f1 のデータ転送が 10 行目、f2 のデータ転送が 12 行目で開始されます。オフロードは計算を開始しません。f1 と f2 をターゲットへ転送するだけです。14 行目で、CPU はターゲットで関数 foo の計算を開始します。この関数は、直前に転送が開始されたデータ f1 と f2 を使用します。ターゲットのオフロード領域の実行は、f1 と f2 の転送が完了した後に開始されます。変数 result は、計算結果を返します。
01 integer, parameter:: n=4086
02 real, allocatable :: f1(:), f2(:), result
03 !dir$ attributes offload:mic :: f1, f2, foo
04 integer :: signal_1, signal_2
05 !dir$ attributes align : 64 :: f1
06 !dir$ attributes align : 64 :: f2
07 allocate(f1(n))
08 allocate(f2(n))
09 f1 = 1.0
10 !dir$ offload_transfer target (mic:0) in(f1) signal(signal_1)
11 f2 = 3.14
12 !dir$ offload_transfer target (mic:0) in(f2) signal(signal_2)
13 !dir$ offload begin target(mic:0) wait (signal_1, signal_2)
14 result = foo(n, f1, f2)
15 !dir$ end offload
複数の独立した非同期データ転送が発生する可能性があります。次の例では、offload_transfer プラグマを使用して、f1 と f2 を別々にターゲットに送信しています (10 行目で f1、13 行目で f2)。
01 program main
02 integer, parameter:: n=4086
03 real, allocatable :: f1(:), f2(:), result
04 !dir$ attributes offload:mic :: f1, f2, foo
05 integer :: signal_1, signal_2
06 !dir$ attributes align : 64 :: f1
07 !dir$ attributes align : 64 :: f2
08 allocate(f1(n))
09 allocate(f2(n))
10 !dir$ offload begin target(mic:0) in (f1 ) nocopy (f2) signal(signal_1)
11 call foo(N, f1, f2)
12 !dir$ end offload
13 !dir$ offload_transfer target(mic:0) wait(signal_1) out (f2)
14 end program main
次の例では、浮動小数点配列 in1 と in2 のデータ転送が 15 行目で開始されます。このプラグマは計算を開始しません。in1 をターゲットへ転送するだけです。do ループ内で in1 または in2 のいずれかはターゲットに転送され、転送されたセットの計算が開始されます。20 行目で CPU はターゲットで関数 compute の計算を開始し、in1 を処理するように指示します。24 行目で CPU はターゲットで関数 compute の計算を開始し、23 行目で転送された in2 を処理するように指示します。
次の例は、オフロードの入力をダブルバッファーにしています。
01 module M
02 integer, parameter :: NNN = 100
03 integer, parameter :: count = 25000000
04 integer :: arr(NNN)
05 real :: dd
06 !dir$ attributes offload:mic::arr, dd
07 end module M
08 subroutine do_async_in()
09 !dir$ attributes offload:mic :: compute
10 use m
11 integer i, signal_1, signal_2, iter
12 real, allocatable :: in1(:), in2(:)
13 real, allocatable :: out1(:), out2(:)
14 iter = 10
15 !dir$ offload_transfer target(mic:0) in(in1 : length(count) alloc_if(.false.) free_if(.false.)) signal(signal_1)
16 do i=1, iter
17 if (mod(i,2) == 0) then
18 !dir$ offload_transfer target(mic:0) if(i .ne. iter) in(in2 : length(count) alloc_if(.false.) free_if(.false.)) signal(signal_2)
19 !dir$ offload target(mic:0) nocopy(in1) wait(signal_1) out(out1 : length(count) alloc_if(.false.) free_if(.false.))
20 call compute(in1, out1)
21 else
22 !dir$ offload_transfer target(mic:0) if(i .ne. iter) in(in1 : length(count) alloc_if(.false.) free_if(.false.)) signal(signal_1)
23 !dir$ offload target(mic:0) nocopy(in2) wait(signal_2) out(out2 : length(count) alloc_if(.false.) free_if(.false.))
24 call compute(in2, out2)
25 endif
26 end do
27 end subroutine do_async_in