なぜ今CUDA Tile C++なのか
GPUプログラミングに携わったことのある開発者なら、SIMT(Single Instruction, Multiple Threads) モデルに慣れ親しんでいるでしょう。各スレッドが実行する処理を逐一指定し、ブロックとグリッドサイズを手動で調整する必要がありました。しかし、CUDA 13.3で公開された CUDA Tile C++ は、このパラダイムを根本から覆します。
タイル(Tile)ベースプログラミングは、多次元配列を基本データ単位とし、カーネルがその配列の一部(タイル)単位で演算を実行するように抽象化します。開発者は「スレッドが何をすべきか」ではなく「データをどのように分割し演算するか」に集中すればよく、並列化、非同期処理、メモリ移動などの低レベルな詳細はCUDA Tileコンパイラとランタイムが自動的に処理します。
既にPythonではCUDA 13.1からサポートされていましたが、C++エコシステムでもタイルベースプログラミングが可能になった点が今回のアップデートの核心です。既存の大規模C++ GPUコードベースを維持しながら、最新のNVIDIAハードウェア(Tensor Core、Shared Memory、TMA)の性能を直接ターゲットにせずとも活用できるようになりました。
参考資料: 本記事の詳細は NVIDIA公式ブログ を元に作成しています。

核心理念: ベクトル加算で見るCUDA Tile C++
最も基本的なGPUカーネルであるベクトル加算(Vector Add)をCUDA Tile C++で記述してみましょう。従来のSIMT方式との違いを即座に体感できます。
従来のSIMT方式(CUDA C++)
__global__ void vecAdd(float* A, float* B, float* C, int vectorLength) {
/* 自分のスレッドインデックスを計算 */
int workIndex = threadIdx.x + blockIdx.x * blockDim.x;
if (workIndex < vectorLength) {
/* ベクトル加算を実行 */
C[workIndex] = A[workIndex] + B[workIndex];
}
}
CUDA Tile C++方式
#include "cuda_tile.h"
__tile_global__ void vectorAdd(float* __restrict__ a, float* __restrict__ b,
float* __restrict__ out, size_t n) {
namespace ct = cuda::tiles;
using namespace ct::literals;
// 16バイトアライメント保証(性能最適化)
a = ct::assume_aligned(a, 16_ic);
b = ct::assume_aligned(b, 16_ic);
out = ct::assume_aligned(out, 16_ic);
int bx = ct::bid().x; // ブロックインデックス取得
// 入力配列を1024要素単位のタイルに分割してロード
auto aTile = ct::partition_view{
ct::tensor_span{a, ct::extents{n}},
ct::shape{1024_ic}
}.load_masked(bx);
auto bTile = ct::partition_view{
ct::tensor_span{b, ct::extents{n}},
ct::shape{1024_ic}
}.load_masked(bx);
// 要素ごとの加算
auto oTile = aTile + bTile;
// 結果をストア
auto oView = ct::partition_view{
ct::tensor_span{out, ct::extents{n}},
ct::shape{1024_ic}
};
oView.store_masked(oTile, bx);
}
主な違い:
__tile_global__キーワードでタイルカーネルであることを明示ct::tensor_spanで配列を多次元ビューとしてラップ(C++23std::mdspanと類似)ct::partition_viewで配列をタイルサイズ(ここでは1024)で分割load_masked/store_maskedで境界条件(配列サイズがタイルサイズで割り切れない場合)を自動処理- スレッドインデックス計算コードが完全に消失
main関数でのカーネル実行
int main() {
constexpr size_t N = 2ULL << 25; // 67,108,864要素
constexpr int TILE_SIZE = 1024;
constexpr int BLOCKS = (N + TILE_SIZE - 1) / TILE_SIZE;
// ...(ホスト配列の割り当てと初期化、デバイスメモリの割り当てとコピーは省略)...
// タイルカーネル実行: 第一引数 = タイルブロック数, 第二引数 = 必ず1
vectorAdd<<<BLOCKS, 1>>>(d_a, d_b, d_out, N);
// ...(結果検証、メモリ解放)...
}
注意: タイルカーネル実行時、
<<<BLOCKS, 1>>>の第二引数は必ず 1 にしてください。実際のスレッド数はコンパイラが決定します。
コンパイルコマンド:
nvcc -std=c++20 --enable-tile -arch sm_120 -o vectorAdd vectorAdd.cu
実行結果:
N: 67108864
Max error: 0.000000e+00

発展: 行列積とTensor Coreの活用
ベクトル加算が入門例だとすれば、行列積(Matrix Multiply)はCUDA Tile C++の真価を発揮する場面です。特に ct::mma 関数を通じて Tensor Coreを自動活用できる点が最大の魅力です。
#include "cuda_tile.h"
// 8x24 * 24x16 = 8x16 行列積(K=24, 8の倍数)
__tile_global__ void kernel(float* __restrict__ a, float* __restrict__ b,
size_t length, float* __restrict__ c) {
namespace ct = cuda::tiles;
using namespace ct::literals;
a = ct::assume_aligned(a, 16_ic);
b = ct::assume_aligned(b, 16_ic);
c = ct::assume_aligned(c, 16_ic);
// 行列のshape定義
auto aShape = ct::extents{8_ic, length};
auto bShape = ct::extents{length, 16_ic};
auto cShape = ct::extents{8_ic, 16_ic};
// テンソルスパン生成
auto aSpan = ct::tensor_span{a, aShape};
auto bSpan = ct::tensor_span{b, bShape};
auto cSpan = ct::tensor_span{c, cShape};
// パーティションビュー: aは4x8タイル、bは8x4タイル -> cは4x4タイル
auto aView = ct::partition_view{aSpan, ct::shape{4_ic, 8_ic}};
auto bView = ct::partition_view{bSpan, ct::shape{8_ic, 4_ic}};
auto cView = ct::partition_view{cSpan, ct::shape{4_ic, 4_ic}};
// アキュムレータタイル(4x4、0で初期化)
using f32x4x4 = ct::tile<float, ct::extents{4_ic, 4_ic}>>;
auto accTile = ct::full<float>(0);
auto [xBlock, yBlock, dummy] = ct::bid();
// K dimensionを8ずつストライドして累積乗算
for (auto idx : ct::irange(0, int(length / 8))) {
auto aTile = aView.load_masked(xBlock, idx);
auto bTile = bView.load_masked(idx, yBlock);
accTile = ct::mma(aTile, bTile, accTile); // Tensor Core自動活用!
}
cView.store_masked(accTile, xBlock, yBlock);
}
このコードで ct::mma が実行する 行列積-累算(Matrix Multiply-Accumulate) 演算は、NVIDIA Tensor Coreの中核機能です。従来は wmma 名前空間やPTXアセンブリで直接制御する必要がありましたが、CUDA Tile C++では たった一行のコードで自動マッピングされます。
Nsight Computeプロファイリング
CUDA Tile C++カーネルも従来のSIMTカーネルと同様に ncu でプロファイリング可能です。
ncu -o VecAddProfile --set detailed ./vectorAdd
Nsight Compute GUIで「Tile Statistics」セクションを開くと、タイルブロック数、コンパイラが選択したブロックサイズなど、タイル固有の情報を確認できます。ソースレベルのパフォーマンスメトリクスもそのままサポートされています。

実務適用のアドバイスと注意点
国内開発現場での適用コンテキスト
日本のGPUプログラミング環境は、依然として SIMTモデルに深く根ざしています。特に金融HPC、自動運転スタートアップ、半導体設計企業で大規模CUDA C++コードベースが運用されていますが、CUDA Tile C++導入時には以下の点を考慮すべきです。
- 既存コードとの混在可能:
__global__SIMTカーネルと__tile_global__タイルカーネルを同一プロジェクトで併用できます。段階的なマイグレーションが可能です。 - 依存関係の問題:
cuda_tile.hヘッダはCUDA 13.3以降でのみ提供されます。社内ビルドシステムが最新CUDAツールキットをサポートしているか事前に確認してください。 - 教育コスト: チームメンバーがタイル抽象化に慣れるまで時間がかかります。特に「スレッドインデックスを直接計算しない」という概念は、初期段階で混乱を招く可能性があります。
本技術の限界
- ハードウェア依存性: CUDA Tile C++はCompute Capability 8.0(Ampere)以上のGPUでのみ動作します。旧型GPU(V100、P100など)では使用できません。
- デバッグの難しさ: コンパイラがスレッド構成を自動決定するため、性能ボトルネック発生時の原因特定が難しい場合があります。Nsight ComputeのTile Statisticsセクションを積極的に活用しましょう。
- すべてのカーネルに適しているわけではない: タイルモデルは規則的なデータ並列処理に最適化されています。条件分岐が多い、または不規則なメモリアクセスパターンを持つカーネルは、依然としてSIMT方式の方が優れている可能性があります。
次のステップとしての学習方向
- CUDA Tile C++公式ドキュメント とAPIリファレンスマニュアルを熟読しましょう。
- NVIDIA Nsight ComputeのTile Statistics機能を活用した性能分析手法を習得してください。
- 既存のSIMTカーネルの中でも、タイルモデルに容易に変換できるパターン(行列演算、畳み込み、FFTなど)から適用することをお勧めします。
合わせて読みたい記事