NVIDIA HPC SDKの利用方法
概要
NVIDIA HPC SDK(以下HPC SDK)はNVIDIA社が提供する開発環境(コンパイラやライブラリなどをまとめたもの)です。
玄界にインストールされているもの以外に、 公式Webページから自分でダウンロードしてきたものをインストールして利用することも可能です。 より新しいコンパイラやライブラリなどを試したい場合は各自で最新版をインストールしてご利用ください。 (システムへのインストールを行わなければ使えないプログラムやライブラリも含まれていることがあります。)
HPC SDKの詳細な情報、最新ドキュメントは NVIDIAのWebサイト を確認してください。
利用準備
HPC SDKを利用するには、あらかじめmoduleをloadしておく必要があります。 HPC SDKに含まれるコンパイラやライブラリを用いて作成したプログラムをジョブとして実行する際は、 ジョブスクリプト内でも同じmoduleのloadが必要です。
はじめに nvidia
モジュール(nvidia/23.9
モジュール)をloadしてください。
MPIを使う場合はさらに nvhpcx
または nvompi
モジュールもloadする必要があります。
( nvidia
モジュールをloadするとavailで見えるようになります。)
特にこだわりが無い場合は nvhpcx/23.9-cuda12
の利用を推奨します。
|
コンパイラの利用方法
HPC SDKにはCPUやGPUに対応した複数のコンパイラが含まれています。 以下ではそれらの基本的な使い方を示します。
コンパイラの種類
HPC SDKの提供するコンパイラと対応する並列化手法の対応は以下の通りです。
- nvc/nvc++ : C/C++コンパイラ。CPU向けにもGPU向けにも利用可能。OpenMPやOpenACCに対応。
- nvfortran : Fortranコンパイラ。CPU向けにもGPU向けにも利用可能。OpenMPやOpenACCに加えて、CUDA Fortranにも対応。
- nvcc : CUDA Toolkitに含まれているものと同じ。(HPC SDKの中に特定バージョンのCUDA Toolkitも同梱されている。)
コンパイルに利用するコマンドはMPIの有無により以下に対応します。 nvccについてはCUDAの利用方法のページを参照してください。
プログラミング言語 | コンパイルコマンド | |
---|---|---|
非MPI並列 | Fortran | nvfortran |
C | nvc | |
C++ | nvc++ | |
MPI並列 | Fortran | mpifort |
C | mpicc | |
C++ | mpic++ |
主な共通のコンパイルオプションとしては以下が利用できます。
詳細はコンパイルオプションに -help
を指定すると確認できます。
コンパイル時オプション | 説明 |
---|---|
-c | オプジェクトファイルの作成までを行います。(実行可能ファイルの作成を行いません。) |
-o filename | 出力される実行可能ファイル/オブジェクトファイル名をfilenameに変更します。デフォルトの実行ファイル名は a.out です。 |
-O | コンパイラが行う最適化の度合いを指定します。最適化を基本的に行わない-O0 から、数字が大きくなるほど最適化対象が増加する-O1 , -O2 , -O3 , -O4 の他、-O や-Ofast もあります。特に-O3 以上は対象プログラムによっては効果がなかったり逆効果だったりするとも言われています。さらに、基本的な(多くの場合に高速となる)最適化オプションとして -fast も用意されています。各最適化オプションの詳細は-help で確認してください。 |
-acc | OpenACC指示文を有効にします。 |
-mp | OpenMP指示文を有効にします。GPUを対象とする場合は-mp=gpu と指定してください。 |
-gpu | OpenACCやOpenMPを用いる場合に対象GPUの詳細な指定を追加できます。debug, pinned, managed, deepcopy, fastmathなど様々なオプションとその組み合わせが利用可能です。H100 GPU向けコードのみの生成を行わせる場合はcc90 (-gpu=cc90) を組み合わせてください。 |
-tp | 最適化対象とするCPUを指定します。玄界では-tp=sapphirerapids (Intel Xeon SapphireRapids)または-tp=native (コンパイル環境と同じCPU)を推奨します。 |
-Minfo | コンパイラが最適化を行う際に詳細情報を出力させることができます。-Minfo=all を指定すれば全てのメッセージが出力されます。GPU化に関する情報だけ確認したい場合は-Minfo=accel が推奨です。 |
以下にいくつかのコンパイル例を示します。
- OpenMPプログラムをコンパイルする(CPU向け、非MPI版)
|
- OpenMPプログラムをコンパイルする(GPU向け、非MPI版)
|
- OpenACCプログラムをコンパイルする(GPU向け、非MPI版)
|
- MPI + OpenMPプログラムをコンパイルする(CPU向け)
|
- MPI + OpenMPプログラムをコンパイルする(GPU向け)
|
- MPI + OpenACCプログラムをコンパイルする(GPU向け)
|
バッチジョブの実行方法
HPC SDKを用いたプログラムを実行するバッチジョブスクリプトの例を示します。
OpenMPを用いたCPUプログラム(ノード内CPU実行の例)
|
ノードグループAの4CPUコアを用いたOpenMP並列プログラムの実行例です。
OMP_NUM_THREADS
環境変数で実行スレッド数を指定します。
割り当てられるコア数を超えない範囲でスレッド数を指定することを強く推奨します。
OpenACCプログラムまたはOpenMPを用いたGPUプログラム(1サブGPU実行の例)
|
ノードグループBの1サブGPUを用いた、OpenACCまたはOpenMPによるGPU並列化プログラムの実行例です。
OpenACC実行時は環境変数NVCOMPILER_ACC_NOTIFY
やNVCOMPILER_ACC_TIME
をセットしておくことで、OpenACCによりGPUを用いた計算が行われていることを簡単に確認できます。
OpenMPにはそのような環境変数がないため、プロファイラなど(後述)で確認すると良いでしょう。
MPI + OpenMPを用いたCPUプログラム(1ノード内8プロセス実行の例)
|
ノードグループAの1ノードを全て使ったMPI+OpenMPハイブリッド並列化プログラムの実行例です。 ノードグループAは60コアCPUが2ソケット搭載されており、各ソケットのコアとメモリは4つのサブNUMAに分かれているため、各サブNUMAに1プロセス配置され、プロセスあたり15スレッド実行されるようにしました。
最適なプロセスとスレッドの割り当て方については別途解説ページを作成予定です。
MPI + OpenMPを用いたCPUプログラム(2ノード×1ノード内8プロセス実行の例)
|
上述した1ノード実行用のスクリプトは、
-L node=1
で指定している必要ノード数と、
mpirun
の-n
で指定している総プロセス数を適切に増やせば、
複数ノード実行へ簡単に拡張できます。
この記述方法では、ノードに必要なだけのプロセスを配置してから、
次のノードへプロセスを配置します。
(はじめに各ノードに1プロセス目を配置、次に各ノードに2プロセス目を配置、
という動作にはなりません。)
MPI + OpenACCまたはOpenMPを用いたGPUプログラム(1ノード内4GPU実行の例)
複数のGPUを利用したプログラムとその実行にはいくつかの方法がありますが、 最もわかりやすく使いやすいと思われるのが 「OpenACCやOpenMPで1GPUを操作するプログラムをMPIにより並列化する。 1MPIプロセスと1GPUを対応づける。」という考え方です。 ノードグループBの各ノードは2基のCPUと4基のGPUを搭載しており、 各CPUは2つのサブNUMAに分かれていることから、 1プロセスあたり1サブNUMAと1GPUを担当するような割り当てにするのが適切です。 ノードグループCの各ノードは2基のCPUと8基のGPUを搭載しており、 各CPUは4つのサブNUMAに分かれていることから、 やはり1プロセスあたり1サブNUMAと1GPUを担当するような割り当てにするのが適切です。 (サブNUMAと対応しているCPUコアの数がノードグループによって異なる点に注意が必要です。)
以下はノードグループBの1ノードに搭載された4GPUを使ったジョブ実行の例です。
job_openacc2.sh
では、PJMオプションでは1ノードをフルに使ったジョブの設定(node=1
)を行い、mpirunではサブNUMAあたり1プロセス、
合計4プロセスを起動しています。
13行目では、nvidia-smi -L
でGPUのIDを出力し、利用するGPUの特定に必要な情報のみをテキストファイルに書き出しています。
run2.sh
ではOpen MPIのランク番号から通し番号(ID)を生成し、
それを元に利用するGPUを選択してプログラムを起動しています。
これによりMPIプロセスごとに別のGPUが使えるようになります。
なおrun2.sh
には実行権限が付与されている必要があります。
(例 chmod u+x ./run2.sh
)
(gpu.txtというファイルに情報を書き込んで参照している都合上、同じディレクトリで複数のプログラム(ジョブ)を同時に実行すると問題が起きます。 必要に応じてジョブごとにサブディレクトリを作成して実行するなどの工夫をしてください。)
|
|
ジョブスクリプト冒頭のノード数設定とmpirunのプロセス数設定(-n
オプション)を
適切に設定すれば、複数ノードでも同様に実行できます。
ノード内の一部のGPUのみを使いたい場合は、-L node=1
の部分を-L gpu=2
のように
利用するGPU数に変更し、mpirunのプロセス設定も適切に設定してください。
MIGを用いた(複数)サブGPUプログラム
ノードグループBに搭載されたH100 GPUはMIG機能により最大7つのサブGPUに分割して利用することができます。 玄界ではいくつかノードのGPUをMIG機能により7つのサブGPUに分割し、4GPU合計で28サブGPUが見える状態にしています。 分割されたGPUは演算性能が低くメモリ容量が小さなGPUとなりますが、機能としては普通に利用できますので、 デバッグ用などに有効に活用してください。
なお1サブGPUあたりのポイントの消費具合はフルGPUの1/7になります。 サブGPUの理論演算性能やメモリ量はフルGPUの1/7よりも若干低い点には注意してください。
サブGPUを使ってプログラムを実行する際は、リソースグループとしてb-batch-mig
かb-inter-mig
を指定してください。
gpu=
で指定した数量はフルGPUではなくサブGPUの数となります。
1ノードあたり28サブGPUありますので、最大で28を指定することができます。
ただしこれらのリソースグループでは複数のサブGPUを要求した際にそれらが同一の物理GPU上に割り当たる保証がありません。
ある程度は同一の物理GPUを確保しようとしますが、
資源の空き状況によってはGPU数に7以下を指定しても異なる物理GPU上のサブGPUが確保されてしまうことがあります。
以下に4サブGPUを用いたインタラクティブジョブにて異なる物理GPU上のサブGPUが割り当たった例を示します。 この例ではCPUコアは同じソケット側のみが割り当たっていますが、ノードの利用状況によってはソケットにまたがって割り当たる可能性もあります。
|
ライブラリの利用方法
HPC SDKには様々なライブラリが含まれています。
Version 23.9に含まれているライブラリ
- cuBLAS
- cuBLASMp
- cuTENSOR
- cuSOLVER
- cuSOLVERMp
- cuFFT
- cuFFTMp
- cuRAND
- NCCL
- NVSHMEM
これらを利用する際は、必要に応じてヘッダファイルの参照やコンパイル時のオプション追加が必要です。 一部の例を紹介しますが、詳細は公式ドキュメントやサンプル等を参照してください。
cuBLASを使う例
CUDA CからcuBLASを使う場合は、cublas_v2.h
のincludeとcublasのリンク(nvccコンパイラに-lcublas
オプションを追加)が必要です。
C言語 + OpenACC/OpenMP(GPU)からcuBLASを使う場合も同様に、cublas_v2.h
のincludeとnvcコンパイラに対する-lcublas
オプションの追加が必要ですが、
これだけでは-lcublas
でリンクするライブラリファイル(libcublas.so)を見つけられずにエラーが発生します。
解決のためには、cuda moduleもloadするか、HPC SDKディレクトリ内にあるlibcublas.soのパスを-Lオプションで与える必要があります。
(-L/home/app/nvhpc/23.9/Linux_x86_64/23.9/math_libs/lib64/
を指定してください。)
CUDA FortranからcuBLASを使うには、use cublas
の記述とcublasのリンク(nvfortranコンパイラに-cudalib=cublas
オプションを追加)が必要です。
Fortran + OpenACC/OpenMP(GPU)からcuBLASを使う場合も同様です。
その他のツールの利用方法
HPC SDKには上記の他にもデバッガやプロファイラなどいくつかのツールが含まれています。 ここでは多くの玄界ユーザにとって便利であると思われるデバッガやプロファイラの使い方を簡単に紹介します。
デバッガの使い方
GPUカーネルのデバッグにはcuda-gdb
が使用できます。
gdb
とほぼ同様の使い方でGPUカーネルのデバッグが行えます。
cuda-gdb
を使う際は、まずデバッガ向けのオプションを付けてプログラムをコンパイルします。
nvcやnvfortranを使う場合は-g
や-gopt
を
(-g
は最適化が無効化される、-gopt
は最適化が無効化されない)、
nvccを使う場合は-g
や-G
(-g
はホストコード向け、-G
はデバイスコード向けの
デバッグ情報生成有効化オプション)を指定します。
|
cuda-gdb
ではスレッドごとのトレースなどGPU向けの機能がサポートされています。
詳細はドキュメントをご確認ください。
プロファイラの使い方
パフォーマンス分析ツール(プロファイラ)としてNVIDIA Nsight SystemsやNVIDIA Nsight Computeが利用できます。 Nsight Systemsはプログラム全体の性能、Nsight ComputeはGPUカーネルの詳細な把握に役立ちます。
以下では基本的な使い方のみを紹介します。 詳細はマニュアル等(Nsight Systems、 Nsight Compute)を参照してください。
Nsight Systemsの使い方
情報の収集はnsys
プログラムで行います。
実行例(ジョブスクリプト内)
|
-o
オプションでプロファイル結果を書き出すファイル名を指定します。
上記の例ではバッチジョブシステムが各ジョブに設定するジョブID情報を元にファイル名を決めています。
実際にはout_82448.nsys-rep
のようなファイルが作られます。
生成されたnsys-rep
ファイルを
Nsight Systems (GUIプログラム)
で開くとプログラムの情報を閲覧できます。
Nsight Systems
はログインノード上でもnsys-ui
コマンドで起動可能です(X転送が必要です)が、
Windows、Linux、macOS向けのクライアントが配布されているため、
nsys-rep
ファイルをダウンロードして手元のPCで閲覧した方が快適に使えることが多いでしょう。
--stats=true
は必須ではないオプションです。
このオプションを付けておくとプログラム実行終了時にプロファイル結果がテキスト表示されます。
対象となる情報が多いとそれだけジョブ実行時間が伸びる点には注意してください。
Nsight Computeの使い方
情報の収集はncu
プログラムで行います。
実行例(ジョブスクリプト内)
|
-o
オプションでプロファイル結果を書き出すファイル名を指定します。
上記の例ではバッチジョブシステムが各ジョブに設定するジョブID情報を元にファイル名を決めています。
実際にはout_82449.ncu-rep
のようなファイルが作られます。
生成されたncu-rep
ファイルを
Nsight Compute (GUIプログラム)
で開くとプログラムの情報を閲覧できます。
Nsight Compute
はログインノード上でもncu-ui
コマンドで起動可能です(X転送が必要です)が、
Windows、Linux、macOS向けのクライアントが配布されているため、
ncu-rep
ファイルをダウンロードして手元のPCで閲覧した方が快適に使えることが多いでしょう。
ログインノード上でncu-ui
を実行する際は、さらに以下の設定が必要です。
ulimit -v 26000000
(仮想メモリの上限を上げています。エラーメッセージがでて終了してしまう場合は、28000000など、より大きな値を試してみてください。)