前書き
久々にNVIDIAのCUDAを使う事になったので色々と使い方を調べて知識を更新する事にした。開発者向けの資料は https://docs.nvidia.com/cuda/index.html
なおCUDAの初期版がリリースされたのが June 23, 2007; 13 years ago らしい。
NVIDIAのGPUと同じような演算性能のハードウェアをPCで扱うとなるとAMDのGPUをROCmで扱うのだろうけどそちらはまだ扱った事が無い。資料は https://rocmdocs.amd.com/en/latest/
Intelも来年にXe-HPGのGPUをリリースするみたいだけれど10年後に継続しているだろうか?oneAPIのData Parallel C++という言語でコードを書くようだ。CPUやFPGA向けにも同じ言語やAPIを使えるらしい。
CUDAのAPIには Runtime API と Driver API が存在していて Driver API の方がローレベルなAPIで Runtime API は Driver API を使って実装しているらしい。
https://stackoverflow.com/a/254521
上のページに分かりやすくまとめられていた。
調査
自分の理解を深めるために色々なAPIを調査してメモをしていく。ほぼレファレンスに書かれている事の和訳になってしまうけれど…。
現時点で最新の CUDA Toolkit v11.2.0 が参照元。
5. Modules
色々なモジュールがあるので気になるものについてメモ。メモリ関連について今のところ一番気になるけれどその他についてはまだまださっぱりな状態。
5.1. Device Management
5.3. Error Handling
エラー情報の取得
5.4. Stream Management
ストリームの管理用API
5.5. Event Management
イベント管理用API
5.7. Execution Control
実行制御用
5.9. Memory Management
この中からメモリ確保用のAPIについて列挙してみる。
- cudaMalloc
- cudaMalloc3D
- cudaMalloc3DArray
- cudaMallocArray
- cudaMallocHost
- cudaMallocManaged
- cudaMallocMipmappedArray
-
cudaMallocPitch
一番基本的なのは cudaMallocでこれはデバイス側にメモリを確保する。ホストとデバイス間でメモリ転送をするには cudaMemcpy を呼び出して明示的に行う必要がある。
cudaMalloc3D は3次元のサイズまで指定して確保する事が出来る。デバイスのメモリアライメント制約を考慮した確保をしてくれるので2Dや3Dの確保を行う場合にはこれか cudaMallocPitch を使う事が推奨されている。
cudaMallocArray はCUDA配列を確保するAPIでテクスチャメモリを確保する為に使うようだ。
cudaMalloc3DArray は cudaMalloc3D のCUDA配列版だろうか?テクスチャメモリを使いたい場合に使用するのかな?
cudaHostAlloc はホストにページロックしたメモリを確保するAPIで malloc で確保したメモリと比べると cudaMemcpy によるメモリ転送が高速に行える。
cudaMallocManaged は CUDA6 から導入された Unified Memory という仕組みで管理されるメモリを確保するAPIが存在する。お手軽そうだけど複数GPU構成の時には色々と注意が必要そう。
developer.nvidia.comに性能を引き出すための解説が書かれていた。
cudaMallocMipmappedArray はMIPMAPされた配列を確保する。
cudaMallocPitch は2D画像用で行アライメントを取ったメモリ確保を行う。
次にメモリ転送用のAPIについて列挙する。
- cudaMemcpy
- cudaMemcpy2D
- cudaMemcpy2DArrayToArray
- cudaMemcpy2DAsync
- cudaMemcpy2DFromArray
- cudaMemcpy2DFromArrayAsync
- cudaMemcpy2DToArray
- cudaMemcpy2DToArrayAsync
- cudaMemcpy3D
- cudaMemcpy3DAsync
- cudaMemcpy3DPeer
- cudaMemcpy3DPeerAsync
- cudaMemcpyAsync
- cudaMemcpyFromSymbol
- cudaMemcpyFromSymbolAsync
- cudaMemcpyPeer
- cudaMemcpyPeerAsync
- cudaMemcpyToSymbol
- cudaMemcpyToSymbolAsync
数が多いのでそれぞれについて調べてメモするのは諦める。非同期系の転送はストリーム関連のAPIと組み合わせて使う事になると思う。
メモリ設定系のAPIは以下の通り
- cudaMemset
- cudaMemset2D
- cudaMemset2DAsync
- cudaMemset3D
- cudaMemset3DAsync
- cudaMemsetAsync
引数で各バイトを初期化する値を指定出来る。
5.11. Stream Ordered Memory Allocator
CUDA 11.2 で追加されたAPIなので使いどころは限られているような気がする。最先端で性能を引き出したい用途向けだろうか…。
ストリーム指定があるAPIで初回の cudaMallocAsync 呼び出しではOSからメモリ確保を行い、cudaFreeAsync 呼び出しのメモリ解放の際はOSに返却せずにCUDAのドライバによって管理されるメモリプールに片づけて、次のcudaMallocAsync 呼び出しでメモリプールから再利用が可能なら再利用を行う。
5.27. Texture Object Management
テクスチャは書き込み専用
5.28. Surface Object Management
サーフェスは書き込みも行える
5.29. Version Management
ドライバとランタイムのバージョン取得用
5.30. Graph Management
なにやらストリームをグラフとして記述し実行する機能のようだ。
複数のカーネル実行の手続きをCPUから一まとめで行えるようにする事でオーバーヘッドを削減する事が出来るとの事。CUDA 10 から使えるようになった機能で一般向けのGPUだとGeForce RTX 2080 Ti とかのGeForce 20 seriesが該当する。これも新しめの機能なので性能を引き出したい最先端の用途向けな気がする。GeForce GTX 1080 Ti の演算性能もまだまだ十分に高いと思うので少し古めのGPUでも動くように対応しておいた方が良さそうな気はする。
5.31. C++ API Routines
C++ template関数群。
5.32. Interactions with the CUDA Driver API
Driver APIとRuntime APIの間の色々なやり取りについて
5.35. Data types used by CUDA Runtime
CUDA Runtimeによって使われるデータ型が列挙されている。
struct、union、Cプリプロセッサの#define、typedef、enum
ここよりヘッダファイル見たほうが良いかも?
雑感
CUDAのAPIは歴史と共に色々と積み重なっていて単純明快とはいえないと思う。デザインが破綻しないように見直しはきちんと行っているようで色々と [DEPRECATED] されているのはそれの表れだろうか…。
当初DirectXやOpenGLのハードウェアアクセラレーションをするのがGPUの目的だったけれど、平行投影でPixel Shaderを使えば汎用の演算用途にも使えると色々な人が気づいてGPGPUというアイデアが提唱されて少ししてからCUDAが登場した。汎用CPUで処理するのには荷が重い演算を一般人が購入出来る価格帯のGPUで行えるように開発用のツールキットが整備されている状況はありがたい。