インテル® Fortran コンパイラー 18.0 デベロッパー・ガイドおよびリファレンス

ポインター変数および割付け変数のメモリー割り当ての管理

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

ここでは、Fortran のポインターターゲットと割付け変数の作成を動的メモリー変数と呼びます。

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

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

入力動的メモリー変数に対するターゲットのメモリー管理

!DIR$ OFFLOAD の in 変数は、デフォルトでは、各動的メモリー変数に新しいメモリーを割り当てます。構造からリターンするときに割り当てられたメモリーは解放されます。オフロード間でデータを保持するには、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.)&
!DIR$ free_if(.false.))

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

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

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

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

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

Module m
integer, allocatable :: p(:)
!dir$ attributes offload:mic :: p
   contains 
        subroutine foo (arg_p, count)
        !dir$ attributes offload:mic :: foo
        integer, allocatable :: 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 (100000) : & 
!dir$ 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)
       !dir$  offload begin target (mic:0) in(iarray : &
       !dir$ alloc_if(.false.))
       iarray=4
      !dir$ end offload

end subroutine bar

次のコードは、CPU とターゲット間の割付け配列で仮引数を使用しています。

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

program main
    use mic_lib
    use mod
    implicit none
    integer i, bar1, bar2

    allocate(arr(1000))
    ptr => arr(1:100)

!   配列をターゲットにコピー
    !dir$ offload begin target(mic:0) in(arr : free_if(.false.))
        arr = (/ (i, i=1,1000) /) ! 1, 2, 3, ...
    !dir$ end offload

    ! bar2 はターゲットで動的に割り当てられたメモリーを使用
    print *, bar2() ! 0

    ! bar1 は上記で割り当てられた "arr" を使用
    print *, bar1(ptr, 100) ! /= 0

end program main

integer function bar2()
     implicit none
     integer i, bar1
     integer, allocatable :: my_p(:)

     allocate (my_p(100))
!    my_p の最初の 100 要素が割り当てられ、ターゲットに転送される ("in" は alloc_if(.true.) を意味する)
     !dir$ offload begin target(mic:0) in(my_p:length(100) &
     !dir$ free_if(.false.))
        my_p = (/ (0, i=1,100) /) ! 0, 0, 0, ...
     !dir$ end offload

     bar2 =  bar1(my_p, 100)
     return
end function bar2

integer function bar1(iarray, len)
     implicit none
     integer :: iarray(len)
     integer len, sum, i

     !dir$ offload begin target(mic:0) in(iarray:length(0) &
     !dir$ alloc_if(.false.) free_if(.false.))
        sum = 0
        do i = 1, len
          sum = sum + iarray(i)
        end do
     !dir$ end offload

     bar1 = sum
     return
 end function bar1