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

ポインター変数のメモリー割り当ての管理

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

オフロードされたプログラムで使用されるポインター変数に対する CPU のメモリー管理は、オフロードされないプログラムと同様です。つまり、offload 宣言子は CPU のメモリー割り当てと解放に影響しません。 通常どおり、プログラマーが指定する必要があります。

!DIR$ OFFLOADin 節と out 節で指定されるポインター変数に対するコプロセッサーのメモリー管理は、コンパイラーとランタイムシステムによって自動的に行われます。

入力ポインター変数に対するコプロセッサーのメモリー管理

!DIR$ OFFLOADin 変数は、デフォルトでは、各ポインター変数に新しいメモリーを割り当てます。 構造からリターンするときに割り当てられたメモリーは解放されます。オフロード間でデータを保持するには、alloc_iffree_if 修飾子を使用して、コプロセッサーのデフォルトメモリー割り当て動作を変更します。

alloc_if 修飾子は、構造がターゲットで実行されるときに in 節のポインター変数がターゲットに新しいメモリーブロックを割り当てるかどうかを制御する論理条件を指定します。 式が .true. と評価されると、節でリストされている各変数に新しいメモリーが割り当てられます。 条件が .false. と評価されると、ターゲットの既存のポインター値が再利用されます。 前のオフロードに対して free_if(.false.) 節を使用し、十分なサイズのメモリーブロックがターゲットの変数に割り当てられていることを確認する必要があります。

free_if 修飾子は、in 節のポインター変数に割り当てたメモリーを解放するかどうかを制御する論理条件を指定します。 式が .true. と評価されると、節でリストされている各変数に割り当てられたメモリーは解放されます。 条件が .false. と評価されると、リストの各変数に割り当てられたメモリーに対する操作は行われません。 割り当てられたメモリーは後の節で再利用できます。

alloc_if および free_if 論理式は、構造がターゲットにオフロードされたときに CPU で評価されます。

出力ポインター変数に対するターゲットのメモリー管理

デフォルトでは、out 変数にはオフロードの開始時にターゲットの新しいメモリーが割り当てられ、オフロードの終了時に割り当てられたメモリーは解放されます。 alloc_if および free_if 修飾子は、このデフォルトの動作を変更します。 式はホストで評価され、コプロセッサーのメモリー割り当てを制御するために使用されます。

出力値がホストで受け取られた場合、メモリー割り当ては行われません。ホストで結果を受け取るには、out 節にリストされた変数は、十分なサイズが割り当てられたメモリーを指している必要があります。

ターゲット上の事前に割り当てられているメモリーへのデータの転送

前のセクションで説明したように、free_if 修飾子を false に設定すると、inoutinout、または nocopy 節のポインター変数は、ターゲットのメモリー割り当てを保持できます。 inoutinout、または nocopy 節を使用して alloc_if(.false.) と指定することで、以降のオフロードでそのメモリーを再利用できます。 ターゲットメモリーが割り当てられると、inoutinout、または nocopy 節のデスティネーションとして使用される CPU ポインター変数の値と関連付けられます。 再利用する場合は、転送のデスティネーションである CPU ポインター変数の値を使用して特定します。ターゲットメモリーが割り当てられたときに使用される CPU アドレスとターゲットメモリーの関連付けは、オフロード・ランタイム・ライブラリーにより自動的に維持されます。この関連付けは、ターゲットメモリーの割り当てまたは解放とともに、作成あるいは削除されます。alloc_if(.true.) free_if(.false.) を使用して割り当て時に関連付けを作成し、free_if(.true.) を使用して解放時に関連付けを削除します。

CPU のスタティック・データへのポインターは特殊なケースです。次の 2 つの条件がどちらも満たされる場合、alloc_if および free_if 修飾子は無視されます。

ターゲットの静的に割り当てられたメモリーは転送のデスティネーションとして使用されます。このターゲットメモリーは動的に割り当てられず、解放されません。

1 つの CPU アドレスと関連付けられるのは、ターゲットメモリーの 1 つのブロックのみです。既存の関連付けを削除する前に同じアドレスに対して alloc_if(.true.) を呼び出して 2 つ目の関連付けを作成しないでください。 新しい関連付けが以前の関連付けに上書きされ、ターゲットのメモリーリークを引き起こす可能性があります。

一致する関連付けが見つからない場合、転送されたポインターに対して free_if(.true.) を呼び出しても処理は行われません。 関連付けの削除は無視されます。関連付けは、1 つの CPU アドレス、特定長、および範囲内の異なる CPU アドレスで作成された別の関連付けから作成することもできます。元のアドレスが異なるため、alloc_if および free_if を使用して個別にターゲットの割り当てを作成できます。

ポインター変数のアライメント

メモリーがターゲットのポインター変数に割り当てられると、ポインターが指しているデータ型の自然境界でアライメントされます。例えば、プログラムで (より厳格なアライメント要件でデータを処理する) アセンブリー・コード、組込み関数、配列表記 (アレイ・ノーテーション) を使用することを予定している場合、データをより大きな境界でアライメントする必要があります。このような場合、align 修飾子を使用してアライメントを指定します。 align 修飾子の演算子は 2 の累乗を評価する整数式でなければなりません。 式はホストで評価され、ターゲットのポインターに割り当てられたメモリーの領域は式の値以上の境界でアライメントされます。出力値がホストで受け取られた場合、メモリー割り当ては行われません。結果を受け取るには、out 節にリストされた変数は、十分なサイズが割り当てられたメモリーを指している必要があります。

データ転送のパフォーマンスが最適になるように、デフォルトで、ポインターによる転送のターゲット・メモリー・アドレスは、CPU データの 64 バイト以内のオフセットと一致するように設定されます。つまり、CPU のソースアドレスが 64 バイト境界を 16 バイト超える場合、ターゲットのデータアドレスも 64 バイト境界を 16 バイト超えます。

align 修飾子を指定すると、デフォルト設定は無視され、指定したアライメントでターゲットメモリーがアライメントされます。 高速なデータ転送の利点を活用し、ターゲットで必要なアライメントを得るためには、CPU データがターゲットで要求されるアライメントと同じ境界でアライメントされていることを確認してください。同じ境界でアライメントすることで、高速なデータ転送の要件とターゲットデータのアライメントの要件が満たされます。

次の例は、コプロセッサーがデータを保持しない、デフォルトの動作を示しています。

コンパイラーが、オフロードで処理するデータを割り当てて解放します。allocfree 修飾子は必要ありません。

real, dimension(:), pointer :: p
!DIR$ ATTRIBUTES OFFLOAD:mic :: p
real, dimension(10), target :: targ
!DIR$ ATTRIBUTES OFFLOAD:mic :: targ

p->targ
!DIR$ OFFLOAD TARGET (mic) in (p)

次の例は、オフロード間でコプロセッサーがデータを保持する場合を示しています。

次のコードは、このオフロードの一部として p にメモリーを割り当て、オフロード後に p に割り当てたメモリーを保持します。

ALLOC はデフォルトで、明示的に指定する必要はないことに注意してください。

!DIR$ OFFLOAD TARGET (mic) in (p : alloc_if(.true.) free_if(.false.))

次のコードは、p に以前割り当てられたメモリーを再利用します。 メモリーに新しいデータのみを転送し、オフロード完了後にメモリーを保持します。

!DIR$ OFFLOAD TARGET (mic) in (p : alloc_if(.false.) free_if(.false.))

次のコードは、p に以前割り当てられたメモリーを再利用します。 ただし、このオフロード後に p に割り当てたメモリーを解放します。

FREE はデフォルトで、明示的に指定する必要はないことに注意してください。

!DIR$ OFFLOAD TARGET (mic) in (p : alloc_if(.false.) free_if(.true.))

次のコードは、ポインターを使用してターゲットのメモリー割り当てを作成しています。その後、ポインター値を別の関数に渡します。ポインター値により、ターゲットメモリーを再利用できます。#pragma offload_transfer 文が配列表記を使用していることに注意してください。 配列表記で変数が指定される場合、length 修飾子は必要ありません。

! ルーチン引数により転送

Module m
integer, pointer :: p(:)
!dir$ attributes offload:mic :: p
   contains 
         subroutine foo (arg_p, count)
        !dir$ attributes offload:mic :: foo
         integer arg_p(:)
        integer count
            …
        end subroutine foo
end module m

program test
use m

allocate(p(100000))
!dir$ offload_transfer target (mic:0) in(p : alloc_if(.true))
call foo (p, 100000)
end

次のコードは、スタティック・データをターゲットに転送します。CPU 変数と一致するターゲットのスタティック・データ割り当てが自動的に使用されます。

! bar が array_cpu_only で呼び出された場合はターゲットの動的メモリーを使用
! bar が array_both で呼び出された場合はターゲットの array_both を使用

module m
   integer array_both(1000)
!dir$ attributes offload : mic :: array_both
  integer array_cpu_only(1000)
end module m

subroutine foo()
use m
          call bar(array_cpu_only, 1000)
           call bar(array_both, 1000)
end subroutine foo

subroutine bar(iarray, count)
      integer iarray(count)
       !dec$  offload begin target (mic:0) in(iarray : alloc_if(.false.))
       iarray=4
      !dec$ end offload

end subroutine bar

次のコードは、スタティックへのポインターと CPU 上に動的に割り当てられたメモリーへのポインターを使用しています。

module mod
integer, target, allocatable :: arr(:)
integer, pointer, dimension(:) :: ptr
!dec$ attributes offload:mic :: ptr, arr
end module mod

program main
    use mic_lib
    use mod
    integer sum

    allocate(arr(1000))
    ptr => arr(1:500)
    !dec$ offload begin target(mic:1) in (arr)
      !! ここに処理を追加
    !dec$ end offload
 
    ! bar2 はターゲットの動的に割り当てられたメモリーを使用
    print *, bar2()
    
    ! bar1 は上記で静的に割り当てられた "arr" を使用
    print *, bar1(ptr, 500)

end program main

integer function bar2()
     integer, allocatable :: my_p(:)
     integer sum

     allocate (my_p(500))
     !dec$ offload begin target(mic:1) in (my_p:length(500) free_if(.false.))
           !! ここに処理を追加
     !dec$ end offload
     bar2 =  bar1(my_p, 500)
end function bar2

integer function bar1(iarray, len)
     integer iarray(len)
     integer sum
     !dec$ offload begin target(mic:1) nocopy(iarray:length(len) &
     &  alloc_if(.false.) free_if(.false.))
        do i = 1, len
          sum = sum + iarray(i)
        end do
     !dec$ end offload
     bar1 = sum
 end function bar1

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