nvcc コンパイラに --generate-code
オプションを指定することで特定環境向けの(おそらく最適な)コードを生成するように指示できる。
対象にしたい Compute Capability (CC) の番号を arch と code のサブオプションに指定する。お使いの NVIDIA GPU の CC は CUDA GPUs | NVIDIA Developer から調べられる。
$ nvcc --generate-code arch=compute_30,code=sm_30
arch には 'virtual' architecture (compute_xx) を指定する。これは PTX コードを生成する時に使われる。
ある CC 向けに生成された PTX コードは、同じかそれより新しい CC 向けのバイナリにコンパイルすることができる。例えば CC5.0 (compute_50) 向けにビルドした PTX コードは CC6.0 の環境でも(最適ではないにしろ)使うことができるが、CC4.0 の環境では使うことができない。
code には 'real' architecture (sm_xx) を指定する。これは PTX コードから sass ないしは cubin を生成する時に使われる。
cubin はメジャーバージョンの互換性は保証されない. 例えば CC5.0 (sm_50) 向けにビルドしたバイナリは CC6.0 の環境で動作することは保証されないし、CC4.0 の環境では動作しない。
複数の CC に対して最適化しておきたい場合は複数指定する。その分コンパイル時間は伸びる。
$ nvcc --generate-code arch=compute_30,code=sm_30 --generate-code arch=compute_50,code=sm_50
このようにして複数の cubin を含めることができるので fatbin と呼ばれている。
fatbin に含めるものを指定
code に real architecture (sm_xx) を指定した場合は fatbin に cubin が含まれる。
$ nvcc --generate-code arch=compute_30,code=sm_30
fatbin に PTX を入れておくこともできる。その場合は code に real architecture (sm_xx) ではなく virtual architecture (compute_xx) を指定する。
$ nvcc --generate-code arch=compute_30,code=compute_30
この場合、cubin はいつ作られるかというと、実行時にJITされる。実行時の JIT は1並列で行われるのでけっこう遅い。一度 JIT したものはファイルシステムにキャッシュされるので2回目の実行からは速い。オプションなしのデフォルト動作時もJITしている。
fatbin に PTX と cubin の両方を入れて置きたい場合は、code に compute_xx と sm_xx の両方を指定する。
$ nvcc --generate-code arch=compute_30,code=compute_30 \
--generate-code arch=compute_30,code=sm_30
この例では、実行環境が sm_30 であれば、その cubin をそのまま使うし、CC が新しい環境であれば PTX コードを JIT して使う。古い環境では動かない。
ちなみに短縮 -arch
オプションで、-arch=sm_30
と指定した場合、この動作と同等の意味になる。