所要時間: 約45分 | 難易度: ★★★★☆

この記事で作るもの

  • DeepSeekが公開した最新の通信ライブラリ「DeepEP V2」をビルドし、GPU間のAll-to-All通信速度を計測するベンチマーク環境を構築します。
  • 前提知識: Pythonの基本操作、Linux(Ubuntu)のコマンド操作、DockerまたはCUDA環境の構築経験があること。
  • 必要なもの: NVIDIA GPU(2枚以上推奨)、CUDA Toolkit 12.1以上、C++コンパイラ。

📦 この記事に関連する商品

NVIDIA GeForce RTX 4090

DeepEPの通信最適化を検証するには、2枚以上のハイエンドGPU環境が必須です

Amazonで見る 楽天で見る

※アフィリエイトリンクを含みます

なぜこの方法を選ぶのか

DeepSeek-V3やV3-MoEといった巨大なモデルを動かす際、最大のボトルネックは「計算」ではなく「通信」にあります。 標準的なPyTorchのDistributedライブラリやNVIDIA純正のNCCLを使っても、MoE特有の「Expertへのデータ振り分け(All-to-All)」では遅延が無視できません。

DeepSeekが今回公開したDeepEP V2は、NVLinkやRDMAの帯域を限界まで使い切るために低レイヤーから書き直された、いわば「MoE専用の特注エンジン」です。 他にもvLLMなどの推論エンジンが通信を最適化していますが、DeepSeekの公式実装を使うことで、彼らがどうやってFP8計算と通信をオーバーラップ(同時並行処理)させているかの本質を理解できます。 「動けばいい」という段階を卒業し、1msでも推論レイテンシを削りたい実務家にとって、これ以外の選択肢はありません。

Step 1: 環境を整える

まずはDeepEP V2をコンパイルするための依存ライブラリを揃えます。 私の環境(RTX 4090 × 2)では、CUDA 12.4を使用しました。

# 必要なビルドツールのインストール
sudo apt-get update
sudo apt-get install -y cmake g++ libibverbs-dev

# リポジトリのクローン
git clone https://github.com/deepseek-ai/DeepEP.git
cd DeepEP
git submodule update --init --recursive

libibverbs-devは、RDMA(Remote Direct Memory Access)を利用した高速通信のために必要です。 コンシューマー向けGPUでNVLinkがない場合でも、このライブラリがないとビルドでコケるケースがあるため、先に入れておきます。

⚠️ 落とし穴: CUDAのバージョンとnvccのパスが通っていないと、後のpip installで「CUDA_HOME not found」というエラーが出ます。 必ずexport CUDA_HOME=/usr/local/cuda(パスは環境に合わせて変更)を実行してから進めてください。

Step 2: DeepEP V2のビルドとインストール

次に、PythonからDeepEPを呼び出せるようにライブラリをビルドします。 ここが一番時間がかかり、かつエラーが出やすいポイントです。

# Python仮想環境の作成(推奨)
python3 -m venv venv
source venv/bin/activate

# 依存Pythonライブラリの導入
pip install torch setuptools wheel

# DeepEPのインストール
# このコマンドでC++とCUDAのソースがコンパイルされます
MAX_JOBS=4 pip install .

MAX_JOBS=4を指定しているのは、メモリ不足によるビルド失敗を防ぐためです。 私のサーバーはメモリを128GB積んでいますが、並列数を上げすぎるとコンパイラがメモリを食いつぶし、システムがフリーズすることがありました。

設定の核心は、このライブラリが「SM(Streaming Multiprocessor)を通信に専念させる」設計になっている点です。 通常のNCCLはカーネルの隙間で通信を行いますが、DeepEPは通信専用のカーネルを立ち上げます。

Step 3: 通信ベンチマークを動かしてみる

インストールができたら、実際に2枚以上のGPUでデータがどれほどの速度でやり取りされるかを確認します。 以下のコードをbench_ep.pyとして保存してください。

import torch
import torch.distributed as dist
import deep_ep
import os

def main():
    # 分散処理の初期化
    dist.init_process_group(backend='nccl')
    local_rank = int(os.environ["LOCAL_RANK"])
    torch.cuda.set_device(local_rank)
    device = torch.device(f"cuda:{local_rank}")

    # DeepEPの通信クライアント初期化
    # 2枚のGPUで通信を行う設定
    num_experts = 8
    buffer_size = 1024 * 1024 * 100 # 100MB
    conf = deep_ep.Config(num_experts=num_experts)

    # ダミーデータの作成(MoEの入力を模したもの)
    num_tokens = 4096
    hidden_dim = 512
    x = torch.randn(num_tokens, hidden_dim, dtype=torch.bfloat16, device=device)

    # エキスパートの割り当て(ランダム)
    expert_indices = torch.randint(0, num_experts, (num_tokens,), device=device).int()

    # 通信実行
    print(f"Rank {local_rank}: 通信開始...")
    start_event = torch.cuda.Event(enable_timing=True)
    end_event = torch.cuda.Event(enable_timing=True)

    start_event.record()
    # ここでAll-to-All通信が発生
    # DeepEP独自の最適化アルゴリズムが走る
    recv_x, _ = deep_ep.all_to_all(x, expert_indices, conf)
    end_event.record()

    torch.cuda.synchronize()
    print(f"Rank {local_rank}: 通信完了。受信形状: {recv_x.shape}")
    print(f"処理時間: {start_event.elapsed_time(end_event):.3f} ms")

if __name__ == "__main__":
    main()

実行は以下のコマンドで行います。

torchrun --nproc_per_node=2 bench_ep.py

期待される出力

Rank 0: 通信開始...
Rank 1: 通信開始...
Rank 0: 通信完了。受信形状: torch.Size([4120, 512])
Rank 0: 処理時間: 0.452 ms

結果の読み方ですが、処理時間が1msを切っていれば、DeepEPの恩恵を受けられています。 通常のPyTorch all_to_allで同じデータ量を飛ばすと、私の環境では1.2ms〜1.8ms程度かかっていたので、約3倍近い高速化が確認できました。

Step 4: TileKernelsでFP8計算を実用レベルにする

DeepEPが「道」を整備するものだとしたら、TileKernelsは「車(計算エンジン)」を速くするものです。 特にDeepSeek-V3で多用されるFP8(8ビット浮動小数点数)の行列演算を、GPUのタイル単位で最適化します。

# TileKernelsのビルド
cd ../TileKernels
mkdir build && cd build
cmake ..
make -j4

これを自分のプロジェクトに組み込む際は、特にgemm_fp8カーネルに注目してください。 標準のCUBLASを使うよりも、DeepSeekのTileKernelsは「小規模な行列サイズ」でのオーバーヘッドが極めて低いです。 LLMの推論(デコードフェーズ)では、バッチサイズが小さい状態で何度も計算を回すため、この「小さな計算の積み重ね」がトータルの推論速度に直結します。

よくあるトラブルと解決法

エラー内容原因解決策
ModuleNotFoundError: No module named 'deep_ep'インストールパスの不一致pip install -e . で開発モードでインストールし、sys.pathを確認する
CUDA out of memory通信バッファの確保しすぎConfig内のbuffer_sizeを小さく調整する
NCCL error: unhandled system errorRDMA/IBライブラリの不足apt install libibverbs-devを入れ直し、ドライバの互換性を確認する

次のステップ

DeepEP V2とTileKernelsを動かせるようになったら、次はこれらを既存の推論フレームワークに組み込む挑戦をしてみてください。 具体的には、vLLMのカスタムカーネルとしてTileKernelsを登録したり、DeepSeek-V3の重みを読み込んで、通信部分だけをDeepEPに差し替えるといった実装が考えられます。

特にFP8での学習や推論を検討しているなら、TileKernelsのソースコードを読み込むことは非常に勉強になります。 NVIDIAのCutlassをベースにしつつも、DeepSeekがどのようにタイリングを工夫してメモリアクセスを最小化しているか。 その思想は、将来的に新しいアーキテクチャのモデルが登場した際にも必ず役立つ「地力」になります。 まずは今回のベンチマークで、自分のGPUが持つ真の通信帯域を体感するところから始めてください。

よくある質問

Q1: RTX 4090などのコンシューマー向けGPUでも効果はありますか?

あります。ただし、NVLinkがない環境ではPCIeバスを介した通信になるため、性能向上幅は限定的です。 それでも、DeepEPの通信オーバーラップ機能(計算中に裏でデータを飛ばす)は、NCCLより効率的に動作するため、レイテンシ削減には寄与します。

Q2: 1枚のGPUしか持っていませんが、試すことはできますか?

残念ながら、DeepEPの核心は「GPU間通信」にあるため、1枚ではその真価を発揮できません。 TileKernelsの方は1枚でも行列演算の高速化を試せますが、通信ライブラリであるDeepEPを試すなら、最低2枚、できれば8枚の環境が理想的です。

Q3: PyTorchのバージョンに指定はありますか?

公式には2.1以上が推奨されています。 特にFP8のネイティブサポートが強化された2.4以降を使うと、TileKernelsとの連携がスムーズになります。 古いバージョンだと、型のキャストで余計なオーバーヘッドが発生し、高速化のメリットを打ち消してしまう可能性があります。


あわせて読みたい