GPUワークフローを爆速化!PyTorch CUDA GraphでCUDA Graphを使いこなす


torch.cuda.CUDAGraph は、PyTorch で CUDA グラフを操作するためのクラスです。CUDA グラフは、一連の CUDA カーネルを単一のユニットとして定義およびカプセル化することを可能にし、個別に起動する操作のシーケンスよりも効率的な実行を実現します。

主な機能

  • デバッグ
    • enable_debug_mode() : グラフのデバッグモードを有効にします。
    • debug_dump(path) : 指定されたパスにグラフの詳細な情報をダンプします。
  • メモリ管理
    • pool() : グラフに関連付けられたメモリプールの ID を取得します。
    • set_pool() : グラフに関連付けられたメモリプールを設定します。
  • グラフのキャプチャとリプレイ
    • capture_begin() : CUDA ストリームをキャプチャモードに設定し、グラフの記録を開始します。
    • capture_end() : グラフの記録を停止し、CUDA ストリームを通常のモードに戻します。
    • replay() : 記録されたグラフを指定された CUDA ストリームで実行します。

利点

  • コードの簡潔化
    CUDA グラフは、複雑な GPU ワークフローをより簡潔で管理しやすいコードに変換することができます。
  • メモリ効率の向上
    CUDA グラフは、メモリ割り当てと解放を最適化し、メモリ使用量を削減することができます。
  • パフォーマンス向上
    CUDA グラフは、カーネル起動のオーバーヘッドを削減し、特に小規模な計算量が多いワークロードにおいて、パフォーマンスを大幅に向上させることができます。

import torch
import torch.cuda.CUDAGraph

# デバイスに接続
device = torch.device("cuda")

# グラフを作成
graph = torch.cuda.CUDAGraph()

# グラフのキャプチャを開始
with graph.capture_begin():
    # 計算を実行
    x = torch.randn(1000, device=device)
    y = x + x

# グラフのキャプチャを終了
graph.capture_end()

# グラフを別のストリームで実行
stream = torch.cuda.Stream()
with torch.cuda.stream(stream):
    graph.replay(stream)

# 結果を確認
print(y)
  • CUDA グラフは、ベータ版であり、今後変更される可能性があります。
  • CUDA グラフは、CUDA 10.0 以降が必要です。


import torch
import torch.cuda.CUDAGraph

# デバイスに接続
device = torch.device("cuda")

# ランダムな行列とベクトルを作成
A = torch.randn(500, 500, device=device)
B = torch.randn(500, 500, device=device)
v = torch.randn(500, device=device)

# グラフを作成
graph = torch.cuda.CUDAGraph()

# グラフのキャプチャを開始
with graph.capture_begin():
    # 行列の乗算を実行
    C = torch.matmul(A, B)

    # ベクトルの加算を実行
    y = C + v

# グラフのキャプチャを終了
graph.capture_end()

# グラフを別のストリームで実行
stream = torch.cuda.Stream()
with torch.cuda.stream(stream):
    graph.replay(stream)

# 結果を確認
print(y)

以下のコードは、torch.cuda.CUDAGraph を使用して、畳み込みニューラルネットワークを実行する方法を示しています。

import torch
import torch.cuda.CUDAGraph
import torch.nn as nn

# デバイスに接続
device = torch.device("cuda")

# 畳み込みニューラルネットワークを作成
class CNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.pool = nn.MaxPool2d(2)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.fc = nn.Linear(1600, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = self.pool(x)
        x = self.conv2(x)
        x = x.view(-1, 1600)
        x = self.fc(x)
        return x

# モデルを作成
model = CNN().to(device)

# ランダムな入力データを作成
x = torch.randn(10, 1, 28, 28, device=device)

# グラフを作成
graph = torch.cuda.CUDAGraph()

# グラフのキャプチャを開始
with graph.capture_begin():
    # モデルを実行
    y = model(x)

# グラフのキャプチャを終了
graph.capture_end()

# グラフを別のストリームで実行
stream = torch.cuda.Stream()
with torch.cuda.stream(stream):
    graph.replay(stream)

# 結果を確認
print(y)

このコードは、畳み込みニューラルネットワークを 3 つの層に分割し、それぞれを CUDA グラフ内のカーネルとして実行しています。

これらの例は、torch.cuda.CUDAGraph を使用して様々なタスクを効率的に実行できることを示しています。



手動のカーネル起動

最も基本的な方法は、個々のカーネルを明示的に起動することです。これは、単純なワークフローや、グラフを使用するよりもオーバーヘッドが小さい場合に適しています。

import torch
import torch.cuda

# デバイスに接続
device = torch.device("cuda")

# ランダムな行列とベクトルを作成
A = torch.randn(500, 500, device=device)
B = torch.randn(500, 500, device=device)
v = torch.randn(500, device=device)

# 行列の乗算を実行
C = torch.matmul(A, B)

# ベクトルの加算を実行
y = C + v

# 結果を確認
print(y)

torch.jit

torch.jit は、PyTorch モデルをトレース可能なモジュールに変換するためのツールです。このモジュールは、推論時に効率的に実行することができ、CUDA グラフと同様にパフォーマンスを向上させることができます。

import torch
import torch.jit

# デバイスに接続
device = torch.device("cuda")

# ランダムな行列とベクトルを作成
A = torch.randn(500, 500, device=device)
B = torch.randn(500, 500, device=device)
v = torch.randn(500, device=device)

# モデルを定義
def model(A, B, v):
    C = torch.matmul(A, B)
    y = C + v
    return y

# モデルをトレースしてモジュールを作成
traced_model = torch.jit.trace(model, (A, B, v))

# モデルを実行
y = traced_model(A, B, v)

# 結果を確認
print(y)

ONNX ランタイム

ONNX ランタイムは、様々な深層学習フレームワークで訓練されたモデルを実行するためのオープンな形式です。PyTorch モデルを ONNX 形式に変換し、ONNX ランタイムを使用して効率的に実行することができます。

import torch
import torchvision.models as models
import onnx

# デバイスに接続
device = torch.device("cuda")

# モデルをロード
model = models.resnet18(pretrained=True).to(device)

# ランダムな入力データを作成
x = torch.randn(1, 3, 224, 224, device=device)

# モデルを ONNX 形式に変換
torch.onnx.export(model, x, "model.onnx")

# ONNX ランタイムをインポート
import onnxruntime

# ONNX モデルをロード
ort_session = onnxruntime.InferenceSession("model.onnx")

# モデルを実行
y = ort_session.run(None, {"input": x.numpy()})[0]

# 結果を確認
print(y)

CUDA カーネルを効率的に実行するための代替ライブラリもいくつか存在します。

  • Intel oneAPI DPCPP
    C++ ベースの分散並列プログラミングのためのライブラリ
  • NVIDIA NCCL
    高速なマルチ GPU 通信のためのライブラリ

これらのライブラリは、特定のユースケースにおいて、torch.cuda.CUDAGraphよりも優れたパフォーマンスを提供できる場合があります。

最適な方法の選択

最適な方法は、具体的なワークフローと要件によって異なります。

  • 高度な または 特定の なユースケース: NVIDIA NCCL または Intel oneAPI DPCPP
  • 異なるフレームワーク間でモデルを移植 する必要がある場合: ONNX ランタイム
  • パフォーマンスが重要 であり、モデルがトレース可能 な場合: torch.jit
  • 単純なワークフロー または グラフを使用するよりもオーバーヘッドが小さい場合: 手動のカーネル起動

各オプションの長所と短所を比較検討し、ニーズに合ったものを選択することが重要です。

  • [PyTorch で CUDA グラフ