CUDA Fortran による CUDA 4.0 Multi-GPU プログラミング (3)

(ホスト側メモリ・エリアの属性)

キーワード GPGPU、CUDA Fortran、UVA, GPUdirect

 マルチGPUを備えたシステム上で、CUDA 4.0 からの機能である Unified Virtual Addressing(UVA) を使用する場合、ホスト側メモリ空間の設定の仕方も重要となる。いわゆる、pinned memory とか mapped memory と言ったメモリ・エリアの属性について解説する。
2011年10月12日 Copyright © 株式会社ソフテック 加藤

ホスト側のメモリ割付属性について

 前々回のコラム、CUDA Fortran による CUDA 4.0 Multi-GPU プログラミング (1)(GPUdirect™ V2.0 / peer-to-peer Access) や前回のコラム、CUDA Fortran による CUDA 4.0 Multi-GPU プログラミング (2)(Unified Virtual Addressing の使用法) において、ホスト側のメモリ空間の属性に関して詳しくは説明をしませんでした。CUDA 4.0 以降になってから、デバイス、ホストを問わず同一のメモリアドレス空間を仮想的に使用できるようになったため、ホスト側のメモリエリアに関して、きちんと理解しておく必要があります。ここでは、いわゆる、Pinned Memory (page-locked) と Zero-copy Memory (mapped) について解説します。

(1) Pinned Memory(あるいは、Page-locked Memory)

 Pinned Memory 形式とは、ホスト側 OS で制御されている仮想メモリ管理下でアドレッシングするエリアではなく、メモリ内の一つの空間をプログラムが占有して使用する形態を言います。一般のOSの仮想メモリ管理では、ページングと称して実行プログラムで使用するメモリイメージはディスクに書き出されて(ページングされ)、仮想的なメモリ領域で管理されます。実際の実行においては、こうしたページングされたディスク上の仮想メモリ領域が、その度に物理メモリ上にマッピングされて動作しています。cudaHostAlloc() CUDA API 関数で pinned 属性によるメモリ割付を行うと、そのホスト側のメモリ空間がページングされない領域となり、GPU との間で占有して使用できます。これは、CUDA 4.0 の UVA 配下でホスト側のメモリを使用する時などは、こうした占有状態でホスト側メモリを使用することが必要となります。CUDA プログラミングにおいて、pinned memory 属性にすることによる利点と欠点は、次の通りです。

  • 利点は、ホスト側メモリとの間で CUDAの API である cudaMemcpy...ライクな関数を使用できるようになる点です。
  • 欠点は、ホストシステムの実メモリ上に pinned memory の領域を大きくとると、ホスト側 OS によるシステム全体の性能が劣化する現象が現れます。
integer function cudaHostAlloc(hostptr, size, flags)
type(C_PTR) :: hostptr
integer :: size, flags

 cudaHostAlloc 関数の引数である hostptr は、C 言語型のポインタで指定します。size は、バイト単位で指定します。flags にメモリ割付の属性を指定します。デフォルトは cudaHostAllocDefault ですが、pinned 割付を行う場合は、cudaHostAllocPortable を指定します。また、以下で説明する mapped(pinned) memory の場合は、cudaHostAllocMapped フラグを指定します。

Pinned memory を割り付ける
istat = cudahostalloc( local_ha, sizeof(1.0)*n, cudaHostAllocPortable )
        type(C_PTR) :: hostptr = local_ha  C言語型ポインタ引数であることに注意
        cudaHostAlloc allocates pinned memory on the host. It returns in hostptr the address o
        the pagelocked allocation, or returns an error if the memory is unavailable. Size is
        in bytes. The flags argument enables different options to be specified that affect 
        the allocation. The normal iso_c_binding subroutine c_f_pointer can be used to move 
        the type(c_ptr) to a Fortran pointer.

(2) Zero-copy Memory (あるいは、Mapped Memory)

 Mapped Memory 形式とは、pinned memory の割付状態で、かつ、CUDA デバイスから直接、ホスト側のその pinned memory エリアへアクセス可能な割付状態を言います。CUDA プログラミングにおいて、mapped memory 属性にすることによる注意点は、次の通りです。

  • 利点は、デバイス上の CUDA kernel プログラムから直接、そのメモリエリアを読み書きできる点です。即ち、Unified Virtual Addressing の管理下で操作が可能となります。
  • 欠点は、ホストシステムの実メモリ上に mapped(pinned) memory の領域を大きくとると、ホスト側 OS によるシステム全体の性能が劣化する現象が現れます。
  • mapped memory を使用する際の注意点として、以下のことがあります。CUDA Kernel の実行中、mapped memory の中身は、「undefined」の状態となっています。mapped memory の内容を読むときは、必ず、その前に、cudaThreadSynchronize() で同期を行うことが必要です。また、mapped memory エリアが必要ではなくなった時、必ず、 cudaFreeHost() 関数でエリアをリリースすることが必要です。
ホスト側のメモリに mapped(pinned) ホストメモリを割り付けることを指示する
istat = cudasetdeviceflags(cudaDeviceMapHost)
        
Mapped memory(local_haポインタ)を割り付けて、UVA 下の「一つのデバイス」領域として認識し、
UVA上のポインタ(local_da) に付け替える
istat = cudahostalloc( local_ha, sizeof(1.0)*n, cudaHostAllocMapped )
istat = cudahostgetdevicepointer( local_da, local_ha, 0 )