この記事の対象読者
- Pythonの基本的な文法を理解している方
- 「CUDA」という言葉を聞いたことがあるが、詳しくは知らない方
- ローカルLLMやStable DiffusionなどGPUを使うツールに興味がある方
- GPU開発の全体像を把握したいエンジニア
この記事で得られること
- CUDA Toolkitの構成要素と各コンポーネントの役割の理解
- nvcc(CUDAコンパイラ)の仕組みとコンパイルフローの把握
- CUDA付属ライブラリ(cuBLAS, cuFFT等)の概要と使い分け
- 開発ツール(Nsight Systems/Compute)によるプロファイリングの基礎
- 環境構築から動作確認までの実践的な手順
この記事で扱わないこと
- CUDAカーネルのゴリゴリな最適化テクニック
- マルチGPUプログラミング
- CUDA Cによる本格的なカスタムカーネル開発
1. CUDA Toolkitとの出会い
「PyTorchインストールしたのに、なんでCUDA Toolkitも入れなきゃいけないの?」
GPU環境を構築していて、こんな疑問を抱いたことはないだろうか。私は何度もあった。
最初は「とりあえずcondaで入れておけばいい」くらいの認識だった。しかし、ある日Stable Diffusionの推論速度を最適化しようとしたとき、壁にぶつかった。xformersのビルドに失敗し、エラーメッセージには「nvccが見つからない」の文字。
「nvccって何だ...?」
そこから調べ始めて、CUDA Toolkitが単なる「おまけ」ではなく、GPU開発の根幹を支える「工具箱」だと気づいた。
CUDA Toolkitは、NVIDIAが提供するGPU開発の総合パッケージだ。コンパイラ、ライブラリ、デバッグツール、プロファイラ...GPU開発に必要なものが全部入っている。いわば「GPU職人の道具一式」。
ここまでで、CUDA Toolkitがどんな立ち位置なのか、なんとなくイメージできただろうか。次は、この道具箱の中身を一つずつ見ていこう。
2. 前提知識の確認
本題に入る前に、この記事で使う用語を整理しておく。
2.1 CUDA(Compute Unified Device Architecture)とは
NVIDIAが開発した、GPUで汎用計算を行うためのプラットフォーム。2007年にリリースされ、今ではAI/ML分野のデファクトスタンダードになっている。
2.2 GPU(Graphics Processing Unit)とは
もともとは画像処理専用のプロセッサ。数千のコアを持ち、並列処理が得意。CPUが「少数精鋭の専門家チーム」なら、GPUは「大人数の作業員軍団」。
2.3 コンパイラとは
人間が書いたプログラムを、機械が実行できる形式に変換するソフトウェア。C言語ならgcc、Rustならrustc、そしてCUDAならnvcc。
2.4 ライブラリとは
よく使う機能をまとめた「部品集」。自分で一から書かなくても、ライブラリを呼び出すだけで高度な処理ができる。
これらの用語が押さえられたら、次に進もう。
3. CUDA Toolkitが生まれた背景
3.1 GPUプログラミングの黎明期
2000年代初頭、GPUで科学計算をしようとすると、OpenGLやDirectXといったグラフィックスAPIを「騙して」使う必要があった。計算結果をピクセルの色に変換したり、逆変換したり...正直、地獄だった。
3.2 NVIDIAの決断
2006年、NVIDIAは画期的な決断をした。「GPUを汎用計算に開放しよう」と。そして2007年、CUDA 1.0がリリースされた。これにより、GPUプログラミングは「グラフィックスの裏技」から「正式なプログラミングモデル」へと進化した。
3.3 現在の立ち位置
今やCUDAは、深層学習フレームワーク(PyTorch、TensorFlow)、科学計算、金融シミュレーション、医療画像処理など、あらゆる分野で使われている。CUDA Toolkitはその開発基盤として、年々進化を続けている。
背景がわかったところで、抽象的な概念から順に、具体的なコンポーネントを見ていこう。
4. CUDA Toolkitの全体像
CUDA Toolkitは大きく4つのカテゴリに分類できる。
| カテゴリ | 主なコンポーネント | 役割 |
|---|---|---|
| コンパイラ | nvcc, nvrtc | CUDAコードを実行可能形式に変換 |
| ライブラリ | cuBLAS, cuFFT, cuDNN等 | 高性能な数学関数を提供 |
| ランタイム | CUDA Runtime API, Driver API | GPUとの通信を抽象化 |
| 開発ツール | Nsight Systems/Compute, cuda-gdb | デバッグとプロファイリング |
これを「GPU開発の工具箱」に例えると、こうなる。
CUDA Toolkit(工具箱)
├── nvcc(ドライバー) → コードを組み立てる道具
├── cuBLAS/cuFFT(電動工具) → 重い作業を自動化
├── Runtime API(説明書) → GPUの使い方ガイド
└── Nsight(測定器) → 性能を計測・診断
基本概念が理解できたところで、これらの抽象的な概念を具体的なコンポーネントの解説に落とし込んでいこう。
5. nvcc: CUDAコンパイラの仕組み
5.1 nvccとは何か
nvcc(NVIDIA CUDA Compiler)は、CUDAコードをコンパイルするためのドライバプログラム。実際には複数のツールを内部で呼び出し、複雑なコンパイル処理を一手に引き受けてくれる。
5.2 コンパイルの流れ
CUDAプログラムのコンパイルは、以下の流れで進む。
ソースコード (.cu)
↓
[前処理] デバイスコードとホストコードを分離
↓
[デバイスコンパイル] → PTX(中間表現)→ cubin(バイナリ)
↓
[ホストコンパイル] → g++/clang で通常のC++としてコンパイル
↓
[リンク] デバイスコードをfatbinaryとしてホストコードに埋め込み
↓
実行可能ファイル
5.3 PTX, cubin, fatbinaryって何?
この3つの用語は、CUDAコンパイルを理解する上で重要だ。
| 形式 | 説明 | 例えるなら |
|---|---|---|
| PTX | 仮想的な中間表現。GPUアーキテクチャに依存しない | 設計図 |
| cubin | 特定のGPUアーキテクチャ向けのバイナリ | 完成品 |
| fatbinary | 複数のPTX/cubinをまとめたコンテナ | 詰め合わせセット |
PTXの利点は「前方互換性」。古いCUDA Toolkitでコンパイルしたコードでも、新しいGPUで実行するときにJIT(Just-In-Time)コンパイルされる。
5.4 アーキテクチャ指定の重要性
nvccでは-archオプションでターゲットアーキテクチャを指定する。
# 仮想アーキテクチャ(PTX生成)
nvcc -arch=compute_89 sample.cu -o sample
# 実アーキテクチャ(cubin生成)
nvcc -arch=sm_89 sample.cu -o sample
# 複数ターゲット(推奨)
nvcc -gencode arch=compute_80,code=sm_80 \
-gencode arch=compute_89,code=sm_89 \
-gencode arch=compute_89,code=compute_89 \
sample.cu -o sample
最後の例では、sm_80とsm_89向けのcubinと、将来のGPU向けのPTXを同時に生成している。
nvccの仕組みがわかったところで、次はCUDA Toolkitに含まれるライブラリ群を見ていこう。
6. CUDAライブラリ: 高性能計算の「電動工具」
CUDA Toolkitには、GPU最適化された数学ライブラリが多数含まれている。これらを使えば、自分でカーネルを書かなくても高性能な処理が可能になる。
6.1 主要ライブラリ一覧
| ライブラリ | 正式名称 | 用途 | CPU版の相当品 |
|---|---|---|---|
| cuBLAS | CUDA Basic Linear Algebra Subroutines | 行列演算(GEMM等) | OpenBLAS, MKL |
| cuFFT | CUDA Fast Fourier Transform | 高速フーリエ変換 | FFTW |
| cuSPARSE | CUDA Sparse Matrix | 疎行列演算 | MKL Sparse |
| cuSOLVER | CUDA Solver | 連立方程式、固有値計算 | LAPACK |
| cuRAND | CUDA Random Number Generation | 乱数生成 | - |
| NPP | NVIDIA Performance Primitives | 画像・信号処理 | Intel IPP |
| cuDNN | CUDA Deep Neural Network | 深層学習の基本演算 | - |
6.2 cuBLAS: 行列演算の主役
深層学習の計算の大部分は行列演算。cuBLASはその心臓部を担う。
# PyTorchでの行列積(内部でcuBLASが呼ばれる)
import torch
a = torch.randn(1024, 1024, device='cuda')
b = torch.randn(1024, 1024, device='cuda')
c = torch.mm(a, b) # cuBLASのSGEMM/DGEMMが動く
6.3 cuFFT: 信号処理の定番
音声処理や画像処理でおなじみのFFT。cuFFTを使えばGPUで高速に実行できる。
import torch.fft
signal = torch.randn(1024, device='cuda')
spectrum = torch.fft.fft(signal) # cuFFTが動く
6.4 cuDNN: 深層学習の縁の下の力持ち
畳み込み、正規化、活性化関数など、深層学習に必要な演算を高度に最適化。PyTorchやTensorFlowは内部でcuDNNを呼び出している。
# cuDNNのベンチマークモード(最適なアルゴリズムを自動選択)
torch.backends.cudnn.benchmark = True
ライブラリの概要が把握できたところで、次はRuntime APIとDriver APIの違いを見ていこう。
7. Runtime API vs Driver API
CUDAには2つのAPIレベルが存在する。これが初心者を混乱させる原因の一つ。
7.1 二つのAPIの違い
| 項目 | Runtime API | Driver API |
|---|---|---|
| ヘッダ | cuda_runtime.h |
cuda.h |
| ライブラリ | libcudart.so |
libcuda.so |
| 提供元 | CUDA Toolkit | NVIDIAドライバ |
| 抽象度 | 高い(使いやすい) | 低い(細かい制御可能) |
| 初期化 | 暗黙的 | 明示的 |
| 用途 | 一般的なCUDAアプリ | ランタイム開発、JITコンパイル |
7.2 どちらを使うべきか
結論から言うと、99%の開発者はRuntime APIで十分。
// Runtime API(簡潔)
cudaMalloc(&d_ptr, size);
cudaMemcpy(d_ptr, h_ptr, size, cudaMemcpyHostToDevice);
myKernel<<<grid, block>>>(d_ptr);
// Driver API(冗長だが細かい制御可能)
cuMemAlloc(&d_ptr, size);
cuMemcpyHtoD(d_ptr, h_ptr, size);
cuLaunchKernel(kernel, gridX, gridY, gridZ, blockX, blockY, blockZ, ...);
Driver APIが必要なケースは、以下のような特殊な状況に限られる。
- カスタムランタイムの開発
- 動的なカーネルロード/アンロード
- 複数のCUDAコンテキストの細かい制御
7.3 PyTorchユーザーは気にしなくていい
PyTorchやTensorFlowを使う場合、これらのAPIは完全に隠蔽されている。フレームワークが適切なAPIを選択してくれるので、意識する必要はない。
APIの違いが理解できたところで、次は開発ツールによるデバッグとプロファイリングを見ていこう。
8. 開発ツール: Nsightファミリー
8.1 Nsight Systemsとは
システム全体のパフォーマンスを可視化するプロファイラ。CPU/GPU間のデータ転送、カーネル実行のタイムライン、メモリ使用量などを一目で把握できる。
# プロファイル実行
nsys profile --stats=true python train.py
# 出力例
Time (%) Total Time (ns) Instances Avg (ns) Operation
-------- --------------- --------- --------- --------------------------
45.2 1,234,567 100 12,345 CUDA Kernel: forward_pass
30.1 823,456 50 16,469 CUDA memcpy HtoD
24.7 675,432 50 13,508 CUDA memcpy DtoH
8.2 Nsight Computeとは
個々のCUDAカーネルを詳細に分析するプロファイラ。占有率、メモリスループット、演算スループットなど、カーネルレベルの最適化に必要な情報を提供。
# 特定のカーネルをプロファイル
ncu --set full python inference.py
# roofline analysisを実行
ncu --set roofline python inference.py
8.3 使い分けの指針
パフォーマンス問題の調査フロー:
1. まずNsight Systemsで全体像を把握
↓ ボトルネックのカーネルを特定
2. Nsight Computeで詳細分析
↓ 最適化ポイントを発見
3. 改善 → 再度Nsight Systemsで効果確認
8.4 cuda-gdb: GPUデバッガ
GDBの拡張版。ブレークポイント設定、変数検査、スレッド状態確認など、CPUデバッグと同じ感覚でGPUコードをデバッグできる。
cuda-gdb ./my_cuda_app
(cuda-gdb) break myKernel
(cuda-gdb) run
(cuda-gdb) cuda thread # 現在のGPUスレッド情報
(cuda-gdb) info cuda kernels # 実行中のカーネル一覧
開発ツールの概要を押さえたところで、実際にCUDA Toolkitをインストールして動かしてみよう。
9. 実際に使ってみよう
9.1 環境構築
インストール方法の選択
| 方法 | メリット | デメリット |
|---|---|---|
| 公式インストーラ | 完全なToolkit | システム汚染のリスク |
| conda | 環境分離可能 | バージョンが古い場合あり |
| Docker | 完全な分離 | オーバーヘッド |
9.2 設定ファイルの準備
以下の3種類の環境設定を用意した。用途に応じて選択してほしい。
開発環境用(cuda_env_dev.sh)
#!/bin/bash
# cuda_env_dev.sh - 開発環境用(このままコピーして使える)
# CUDA Toolkit のパス設定
export CUDA_HOME=/usr/local/cuda
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
# デバッグ用設定
export CUDA_LAUNCH_BLOCKING=1 # 同期実行(デバッグしやすい)
export CUDA_VISIBLE_DEVICES=0 # 使用するGPUを限定
# コンパイラ設定
export NVCC_GENCODE="-gencode arch=compute_89,code=sm_89"
echo "CUDA開発環境を設定しました"
echo "CUDA Version: $(nvcc --version | grep release)"
本番環境用(cuda_env_prod.sh)
#!/bin/bash
# cuda_env_prod.sh - 本番環境用(このままコピーして使える)
# CUDA Toolkit のパス設定
export CUDA_HOME=/usr/local/cuda
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
# パフォーマンス最適化設定
export CUDA_LAUNCH_BLOCKING=0 # 非同期実行(高速)
export CUDA_AUTO_BOOST=1 # クロック自動ブースト有効
# cuDNN最適化
export CUDNN_BENCHMARK=1 # ベンチマークモード有効
# マルチGPU設定(必要に応じて変更)
# export CUDA_VISIBLE_DEVICES=0,1,2,3
echo "CUDA本番環境を設定しました"
CI/テスト環境用(cuda_env_test.sh)
#!/bin/bash
# cuda_env_test.sh - CI/テスト環境用(このままコピーして使える)
# CUDA Toolkit のパス設定
export CUDA_HOME=/usr/local/cuda
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
# 再現性重視の設定
export CUDA_LAUNCH_BLOCKING=1
export CUBLAS_WORKSPACE_CONFIG=:4096:8 # 決定論的動作
export CUDNN_DETERMINISTIC=1 # cuDNN決定論的モード
# テスト用に最小リソース
export CUDA_VISIBLE_DEVICES=0
echo "CUDAテスト環境を設定しました(決定論的モード)"
9.3 動作確認コード
"""
cuda_check.py - CUDA Toolkit動作確認スクリプト
使い方: python cuda_check.py
"""
import subprocess
import sys
def check_nvcc():
"""nvccのバージョン確認"""
try:
result = subprocess.run(
['nvcc', '--version'],
capture_output=True,
text=True
)
print("=== nvcc (CUDAコンパイラ) ===")
print(result.stdout)
return True
except FileNotFoundError:
print("ERROR: nvccが見つかりません")
print("CUDA Toolkitがインストールされているか確認してください")
return False
def check_pytorch_cuda():
"""PyTorchからのCUDA確認"""
try:
import torch
print("=== PyTorch CUDA情報 ===")
print(f"PyTorch Version: {torch.__version__}")
print(f"CUDA Available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f"CUDA Version: {torch.version.cuda}")
print(f"cuDNN Version: {torch.backends.cudnn.version()}")
print(f"GPU Count: {torch.cuda.device_count()}")
for i in range(torch.cuda.device_count()):
props = torch.cuda.get_device_properties(i)
print(f"\nGPU {i}: {props.name}")
print(f" Compute Capability: {props.major}.{props.minor}")
print(f" Total Memory: {props.total_memory / 1024**3:.1f} GB")
print(f" SM Count: {props.multi_processor_count}")
return True
except ImportError:
print("PyTorchがインストールされていません")
return False
def check_cuda_libraries():
"""CUDAライブラリの確認"""
try:
import torch
if not torch.cuda.is_available():
return False
print("\n=== CUDAライブラリ動作確認 ===")
# cuBLAS確認(行列積)
a = torch.randn(100, 100, device='cuda')
b = torch.randn(100, 100, device='cuda')
c = torch.mm(a, b)
print("cuBLAS (GEMM): OK")
# cuFFT確認
signal = torch.randn(1024, device='cuda')
spectrum = torch.fft.fft(signal)
print("cuFFT (FFT): OK")
# cuDNN確認(畳み込み)
conv = torch.nn.Conv2d(3, 64, 3, device='cuda')
x = torch.randn(1, 3, 224, 224, device='cuda')
y = conv(x)
print("cuDNN (Conv2d): OK")
# cuRAND確認
rand = torch.rand(1000, device='cuda')
print("cuRAND (Random): OK")
return True
except Exception as e:
print(f"ERROR: {e}")
return False
def main():
print("=" * 50)
print("CUDA Toolkit 動作確認")
print("=" * 50)
results = []
results.append(("nvcc", check_nvcc()))
results.append(("PyTorch CUDA", check_pytorch_cuda()))
results.append(("CUDA Libraries", check_cuda_libraries()))
print("\n" + "=" * 50)
print("確認結果サマリー")
print("=" * 50)
for name, status in results:
mark = "OK" if status else "NG"
print(f" {name}: {mark}")
all_ok = all(status for _, status in results)
sys.exit(0 if all_ok else 1)
if __name__ == "__main__":
main()
9.4 実行結果
上記のコードを実行すると、以下のような出力が得られる。
==================================================
CUDA Toolkit 動作確認
==================================================
=== nvcc (CUDAコンパイラ) ===
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2024 NVIDIA Corporation
Built on ...
Cuda compilation tools, release 12.4, V12.4.131
=== PyTorch CUDA情報 ===
PyTorch Version: 2.2.0
CUDA Available: True
CUDA Version: 12.1
cuDNN Version: 8902
GPU Count: 1
GPU 0: NVIDIA GeForce RTX 4090
Compute Capability: 8.9
Total Memory: 24.0 GB
SM Count: 128
=== CUDAライブラリ動作確認 ===
cuBLAS (GEMM): OK
cuFFT (FFT): OK
cuDNN (Conv2d): OK
cuRAND (Random): OK
==================================================
確認結果サマリー
==================================================
nvcc: OK
PyTorch CUDA: OK
CUDA Libraries: OK
9.5 よくあるエラーと対処法
| エラー | 原因 | 対処法 |
|---|---|---|
nvcc: command not found |
PATHが通っていない | export PATH=/usr/local/cuda/bin:$PATH |
libcudart.so: cannot open |
LD_LIBRARY_PATHの問題 | export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH |
CUDA driver version is insufficient |
ドライバが古い | NVIDIAドライバを更新 |
no kernel image is available |
アーキテクチャ不一致 | 正しい-archオプションでリビルド |
CUDA out of memory |
VRAM不足 | バッチサイズ削減、torch.cuda.empty_cache()
|
基本的な使い方をマスターしたので、次はユースケース別のガイドを見ていこう。
10. ユースケース別ガイド
10.1 ユースケース1: PyTorchでの深層学習
想定読者: 深層学習エンジニア、データサイエンティスト
推奨構成: conda環境 + PyTorch公式バイナリ
サンプルコード:
"""
pytorch_cuda_example.py - PyTorchでのCUDA活用例
"""
import torch
import torch.nn as nn
import time
def benchmark_cuda():
"""CPU vs GPU の性能比較"""
# モデル定義
model = nn.Sequential(
nn.Linear(1024, 2048),
nn.ReLU(),
nn.Linear(2048, 2048),
nn.ReLU(),
nn.Linear(2048, 1024)
)
x = torch.randn(256, 1024)
# CPU実行
model_cpu = model.to('cpu')
x_cpu = x.to('cpu')
start = time.time()
for _ in range(100):
_ = model_cpu(x_cpu)
cpu_time = time.time() - start
# GPU実行
if torch.cuda.is_available():
model_gpu = model.to('cuda')
x_gpu = x.to('cuda')
# ウォームアップ
_ = model_gpu(x_gpu)
torch.cuda.synchronize()
start = time.time()
for _ in range(100):
_ = model_gpu(x_gpu)
torch.cuda.synchronize()
gpu_time = time.time() - start
print(f"CPU Time: {cpu_time:.3f}s")
print(f"GPU Time: {gpu_time:.3f}s")
print(f"Speedup: {cpu_time/gpu_time:.1f}x")
else:
print("CUDA is not available")
if __name__ == "__main__":
benchmark_cuda()
10.2 ユースケース2: カスタムCUDAカーネルのビルド
想定読者: パフォーマンス最適化エンジニア、ライブラリ開発者
推奨構成: 公式CUDA Toolkitフルインストール
サンプルコード:
"""
custom_kernel_build.py - PyTorchでカスタムCUDA拡張をビルド
"""
import torch
from torch.utils.cpp_extension import load_inline
# CUDAカーネルのソースコード
cuda_source = """
__global__ void vector_add_kernel(
const float* a, const float* b, float* c, int n
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
c[idx] = a[idx] + b[idx];
}
}
torch::Tensor vector_add_cuda(torch::Tensor a, torch::Tensor b) {
auto c = torch::empty_like(a);
int n = a.numel();
int threads = 256;
int blocks = (n + threads - 1) / threads;
vector_add_kernel<<<blocks, threads>>>(
a.data_ptr<float>(),
b.data_ptr<float>(),
c.data_ptr<float>(),
n
);
return c;
}
"""
cpp_source = """
torch::Tensor vector_add_cuda(torch::Tensor a, torch::Tensor b);
"""
def build_and_test():
"""カスタムカーネルをビルドしてテスト"""
# JITコンパイル
custom_ops = load_inline(
name='custom_vector_add',
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=['vector_add_cuda'],
verbose=True
)
# テスト
a = torch.randn(10000, device='cuda')
b = torch.randn(10000, device='cuda')
# カスタムカーネルで実行
c_custom = custom_ops.vector_add_cuda(a, b)
# PyTorch標準で実行(検証用)
c_pytorch = a + b
# 結果確認
assert torch.allclose(c_custom, c_pytorch)
print("Custom CUDA kernel works correctly!")
if __name__ == "__main__":
build_and_test()
10.3 ユースケース3: プロファイリングによる最適化
想定読者: パフォーマンスチューニングを行うエンジニア
推奨構成: Nsight Systems + Nsight Compute
サンプルコード:
"""
profiling_example.py - Nsightでプロファイリングする例
実行: nsys profile python profiling_example.py
"""
import torch
import torch.nn as nn
import torch.cuda.nvtx as nvtx # NVIDIAプロファイラ用マーカー
class ProfiledModel(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 64, 3, padding=1)
self.conv2 = nn.Conv2d(64, 128, 3, padding=1)
self.fc = nn.Linear(128 * 56 * 56, 1000)
def forward(self, x):
# NVTXマーカーで各レイヤーを識別
with nvtx.range("conv1"):
x = torch.relu(self.conv1(x))
x = torch.max_pool2d(x, 2)
with nvtx.range("conv2"):
x = torch.relu(self.conv2(x))
x = torch.max_pool2d(x, 2)
with nvtx.range("fc"):
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
def main():
model = ProfiledModel().cuda()
x = torch.randn(32, 3, 224, 224, device='cuda')
# ウォームアップ
for _ in range(10):
_ = model(x)
torch.cuda.synchronize()
# プロファイリング対象の実行
with nvtx.range("inference_batch"):
for i in range(100):
with nvtx.range(f"iteration_{i}"):
output = model(x)
torch.cuda.synchronize()
print("Profiling complete. Check the .nsys-rep file.")
if __name__ == "__main__":
main()
ユースケースが把握できたところで、この記事を読んだ後の学習パスを確認しよう。
11. 学習ロードマップ
この記事を読んだ後、次のステップとして以下をおすすめする。
初級者向け(まずはここから)
- CUDA公式チュートリアルを試す
- サンプルプロジェクトを動かす
中級者向け(実践に進む)
- Nsightツールでプロファイリングを習得
- cuBLAS/cuDNNの直接利用を学ぶ
上級者向け(さらに深く)
- CUDAプログラミングガイドを精読
- カスタムカーネル最適化に挑戦
12. まとめ
この記事では、CUDA Toolkitについて以下を解説した。
- 全体像: コンパイラ、ライブラリ、ランタイム、開発ツールの4カテゴリ
- nvcc: CUDAコードをPTX/cubinにコンパイルする仕組み
- ライブラリ群: cuBLAS、cuFFT、cuDNNなどの役割と使い分け
- API: Runtime APIとDriver APIの違い
- 開発ツール: Nsight Systems/Computeによるプロファイリング
私の所感
正直なところ、CUDA Toolkitを「完全に理解した」と言えるエンジニアは少ないと思う。コンポーネントが多すぎて、全部を使いこなすのは不可能に近い。
でも、それでいい。
大事なのは「どこに何があるか」を把握しておくこと。PyTorchが遅いと感じたらNsightで調べる。カスタムカーネルが必要になったらnvccの使い方を調べる。そういう「地図」があれば、必要なときに深掘りできる。
この記事が、あなたのGPU開発の「地図」になれば幸いだ。