Skip to content

初期化

CUDA 12.0から、cudaInitDevice関数とcudaSetDevice関数は指定されたデバイスに紐付いた、ランタイムとプライマリーコンテキストを初期化する。 これらの関数呼び出しがないと、他のランタイムAPIリクエストを処理する必要がある時にランタイムは暗にデバイス0を使い、自身を初期化する。 ランタイムの関数呼び出しの時間計測とランタイムへの最初の関数呼び出しからのエラーコードを解釈するときに心に留めておく必要がある。 12.0以前では、cudaSetDevice関数はランタイムを初期化せず、アプリケーションは(時間計測とエラーハンドリングのために)他のAPI呼び出しからランタイムの初期化を切り離す、何もしない関数呼び出しcudaFree(0)をよく使っていた。

compute capabilityが2.0以上のデバイスの数はcudaGetDeviceCount関数でわかる。デバイス番号は0から始まり、デバイス数未満までである。

プライマリーコンテキストは、確保されたメモリのリストやデバイスコードを含む、ロードされたモジュールなどデバイスを制御するデータ全てを持つ1。 プライマリーコンテキストはデバイス毎に1つしかなく、CUDAランタイムAPIと共有される。 つまり、アプリケーションのすべてのホストスレッドで共有される。 このコンテキストを作成する時、必要ならデバイスコードをjust-in-timeコンパイルする。 詳しくは、コンテキストにて。

ホストスレッドがcudaDeviceReset関数を呼び出した場合、ホストスレッドが現在操作しているデバイスのプライマリーコンテキストを破棄する。

CUDAインターフェイスはホストプログラムの開始時に初期化され、ホストプログラムの終了時に破棄されるグローバルな状態を持つ。CUDAランタイムとドライバーはこの状態が無効かどうかを検出できないので、明示的か暗黙的かに関わらず、プログラムの開始時と終了時にこれらのインターフェイスを使うと未定義動作になる。

CUDA 12.0時点では、cudaSetDevice()はホストスレッドの現在のデバイスを変更後にランタイムを明示的に初期化する。 以前のバージョンのCUDAでは、cudaSetDevice()が呼び出されてから初めてランタイムの関数が呼ばれるまで、新しいデバイス上のランタイムの初期化を遅延していた。 この変更により、初期化のエラーをcudaSetDevice()の戻り値で確認することがとても重要になった。 エラー処理とバージョン管理のランタイム関数はランタイムを初期化しない。

/src/programming_interface/initialization/main.cu
#include <cassert>

#include <cuda_runtime.h>

int main() {
  // Instruct CUDA to yield its thread when waiting for results from the device.
  unsigned int device_flags = cudaDeviceScheduleYield;
  // Tell the CUDA runtime that DeviceFlags is being set in cudaInitDevice call
  unsigned int flags = cudaInitDeviceFlagsAreValid;
  // Initialize device to be used for GPU executions.
  auto e = cudaInitDevice(0, device_flags, flags);
  assert(e == cudaSuccess);

  // Set device to be used for GPU executions.
  e = cudaSetDevice(0);
  assert(e == cudaSuccess);

  int device;
  e = cudaGetDevice(&device);
  assert(e == cudaSuccess);
  assert(device == 0);

  unsigned int current_flags;
  e = cudaGetDeviceFlags(&current_flags);
  assert(e == cudaSuccess);
  // Flags returned by this function may specifically include cudaDeviceMapHost
  // even though it is not accepted by cudaSetDeviceFlags because it is implicit
  // in runtime API flags.
  assert((current_flags & device_flags) == device_flags);
}