昨日の日記の続き。
引き続き、Tesla P100のハードウェア詳細について見て行く。
注意:GPU初心者が書いている文章のため、間違いが含まれている可能性があります。間違いがあれば、指摘いただければ嬉しいです。
Unified Memory
これまどでのGPUのプログラミングモデルの中では、CPUとGPUのデータやり取りの中で、明示的にGPU側のメモリ確保を行い、データを転送し計算を行い、データを回収するという処理が必要だった。 つまり、GPUとCPUのメモリ空間は完全に分離されていたと言える。
これが、CUDA6(Tesla P100の世代?)では統一されたメモリ空間として与えられる。
メモリ空間はCPUとGPUのどちらからもあくせっすできるようになり、すべてのメモリ空間の制約は、物理メモリ空間のサイズにのみ制約されることになる。
例として、ホワイトペーパーでは以下のプログラムを示していた。左側がCPUのコードだが、まずはメモリ空間を確保し、ソーティングプログラムを実行しその結果を回収する。 CUDA6のプログラミングモデルも同様だ。CPUでGPUのメモリ空間を確保し、その上でCUDAが演算を実行する。さらにその結果を回収するというプログラムが、CPUを使ったプログラミングモデルでも、GPUを使ったプログラミングモデルでもほぼ同じソースコードで実現できるようになった。
以下の図はhttps://devblogs.nvidia.com/parallelforall/inside-pascal/より引用。
また、Pascal GP100のUnified Memoryでは、通常のCPUと同程度である49ビットの仮想アドレス空間を持っている。 さらに、ページフォルトなどの機構をデフォルトでサポートしている。 ページフォルトをサポートしているということは、CUDAシステムのソフトウェアは、カーネルを起動する前にメモリを明示的に確保する必要が無くなる。 CPUで確保したメモリを、GPUでそのまま活用できるということになり、オペレーティングシステムによってサポートされるデフォルトのメモリ割り当て機構(例えばmallocなど)が利用できるようになる。
以下のプログラムがデフォルトのmallocを使ったUnified Memoryの例となる。以下の図はhttps://devblogs.nvidia.com/parallelforall/inside-pascal/より引用。
まとめると、以下のようになる。
- 統一されたメモリマップにより、プログラミングモデルが簡単になる。
- 簡単になることにより、複雑なC++のデータ構造のようなものが、GPUで活用できやすくなる。
- GPUとCPUのデータ転送をオンデマンドに実行できるようになることで、GPUの局所データ性を活用して性能を出しやすくなる。複雑な部分は、CUDAのドライバやランタイムライブラリが担保し、プログラムが複雑になることを防いでいる。
Compute Preemption
GPUプログラム中でプリエンプション(細粒度なタスク切り替え)が可能になる。 これまではGPUのタスク切り替えというとスレッドレベルでしかできなかったが、命令レベルでのタスク切り替えが可能になる。これにより、長いプログラムの間に短いプログラムがタスクレベルで挿入できるようになり、全体のレイテンシを短縮することができるようになる。
また、デバッガのような途中で割り込みを入れてプログラムを監視するようなプログラムも実現しやすくなる。