add-sgl-kernel について
`sgl-kernel` に AOT CUDA カーネルを追加し、SGLang に統合するためのチュートリアルスキルです。要素ごとのスケーリングを例に、実装、登録、Python API、テスト、ベンチマーク、ビルド、検証を扱います。
主な機能
- 01JIT と AOT の配置方針を説明
- 02CUDA 実装と登録を案内
- 03Python API と SGLang ラッパーを追加
- 04pytest テストとベンチマークを作成
- 05カーネルをビルドして検証
活用シーン
- 01要素ごとの CUDA 演算の追加
- 02SGLang ランタイムへのカーネル統合
- 03AOT カーネル変更のテストと性能測定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sgl-project/ sglang --skill add-sgl-kernelインストール後、Claude Code で/add-sgl-kernelを入力して読み込まれたか確認します。
詳細
チュートリアル:sgl-kernel に新しいカーネルを追加する(AOT/重量級)
パブリックオペレーターの名前空間、論理的なグループ化、遅延レジストリメタデータ、テストの配置には、kernel-organization を適用してください。以下の実装チュートリアルは、その API 契約に代わるものではありません。
このチュートリアルでは、単純な要素ごとのスケール演算を AOT カーネルとして追加する方法を説明します。完全なワークフローを示すために、scale(x, factor) = x * factor を実装します。
目標
テンソルの各要素にスカラー係数を掛ける新しい演算を追加します。
- 入力:テンソル
x(CUDA)とスカラーfactor(float) - 出力:
x * factor(要素ごと。インプレース、または事前に割り当てたoutへの出力) - サポートする dtype:FP16(
torch.float16)、BF16(torch.bfloat16)、FP32(torch.float32)DISPATCH_PYTORCH_DTYPE_TO_CTYPE_FLOAT_FP16マクロ(python/sglang/kernels/aot/include/utils.hで定義)を介してディスパッチされます
2 つの基本ルール(必ず守ってください)
- カーネルが CUTLASS やその他の大規模な C++ プロジェクトに依存しない場合は、まず
python/sglang/kernels/jitを優先してください。素早い反復が有効な軽量カーネルでは、これがデフォルトの方法です。 - カーネルが CUTLASS やその他の大規模な C++ プロジェクトに依存する場合、または AOT wheel/torch op 登録フローに含める場合は、
sgl-kernelを優先してください。 - 例外:依存先が
flashinfer、またはflashinfer経由ですでに提供されている CUTLASS の場合でも、カーネルはjit_kernelとして実装できます。
さらに、新しいカーネルにはすべて、次のものを含める必要があります。
- テスト(pytest)
- ベンチマークスクリプト(
sglang.kernels.jit.benchmarkのmarker.do_bench。JIT カーネルに限らず、あらゆる呼び出し可能オブジェクトで動作します)
リポジトリ統合マップ
通常、以下のファイル/領域を変更します。
- 実装:
python/sglang/kernels/aot/csrc/elementwise/scale.cu(適切なサブディレクトリを選びます) - パブリック宣言:
python/sglang/kernels/aot/include/sgl_kernel_ops.h - Torch 拡張の登録:
python/sglang/kernels/aot/csrc/common_extension.cc - ビルド:
python/sglang/kernels/aot/CMakeLists.txt(set(SOURCES ...)) - Python API:
python/sglang/kernels/aot/python/sgl_kernel/とpython/sglang/kernels/aot/python/sgl_kernel/__init__.py - テスト:
python/sglang/kernels/aot/tests/test_scale.py - ベンチマーク:
python/sglang/kernels/aot/benchmark/bench_scale.py
手順 1:csrc/ にカーネルを実装する
適切なサブディレクトリを選びます。
csrc/elementwise/— 要素ごとの演算(今回の例)csrc/gemm/、csrc/attention/、csrc/moe/— その他のカテゴリ
python/sglang/kernels/aot/csrc/elementwise/scale.cu を作成します。
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <torch/all.h>
#include "utils.h" // DISPATCH_PYTORCH_DTYPE_TO_CTYPE_FLOAT_FP16
// scale_kernel: out[i] = input[i] * factor
// Supports float, half (__half), __nv_bfloat16 via template T
template <typename T>
__global__ void scale_kernel(T* __restrict__ out,
const T* __restrict__ input,
float factor,
int64_t n) {
int64_t idx = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
if (idx < n) {
out[idx] = static_cast<T>(static_cast<float>(input[idx]) * factor);
}
}
void scale(at::Tensor& out, const at::Tensor& input, double factor) {
TORCH_CHECK(input.is_cuda(), "input must be a CUDA tensor");
TORCH_CHECK(input.is_contiguous(), "input must be contiguous");
TORCH_CHECK(out.is_cuda(), "out must be a CUDA tensor");
TORCH_CHECK(out.is_contiguous(), "out must be contiguous");
TORCH_CHECK(out.sizes() == input.sizes(), "out and input must have the same shape");
TORCH_CHECK(out.scalar_type() == input.scalar_type(),
"out and input must have the same dtype");
const int64_t n = input.numel();
const int threads = 256;
const int blocks = (n + threads - 1) / threads;
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
const at::cuda::OptionalCUDAGuard device_guard(device_of(input));
// Dispatches over float, float16, bfloat16
DISPATCH_PYTORCH_DTYPE_TO_CTYPE_FLOAT_FP16(input.scalar_type(), c_type, [&] {
scale_kernel<c_type><<<blocks, threads, 0, stream>>>(
static_cast<c_type*>(out.data_ptr()),
static_cast<const c_type*>(input.data_ptr()),
static_cast<float>(factor),
n);
cudaError_t status = cudaGetLastError();
TORCH_CHECK(status == cudaSuccess,
"scale_kernel launch failed: ", cudaGetErrorString(status));
return true;
});
}
重要なポイント:
at::Tensor(PyTorch テンソル)、検証用のTORCH_CHECK、ストリーム用のat::cuda::getCurrentCUDAStream()を使用します- Python ラッパーは薄く保ち、シェイプ/dtype/デバイスの検証は起動処理の直前に C++ で行います
DISPATCH_PYTORCH_DTYPE_TO_CTYPE_FLOAT_FP16はfloat、half(FP16)、__nv_bfloat16(BF16)を扱います- カーネルを起動するたびにデバイスエラーチェックを追加します
- 特定のアーキテクチャでしか動作しないカーネルの場合は、
TORCH_CHECKで制約を適用し、テストではスキップ条件を設定します
手順 2:include/sgl_kernel_ops.h に C++ 宣言を追加する
python/sglang/kernels/aot/include/sgl_kernel_ops.h を編集し、elementwise セクションに追加します。
void scale(at::Tensor& out, const at::Tensor& input, double factor);
手順 3:csrc/common_extension.cc に op を登録する
python/sglang/kernels/aot/csrc/common_extension.cc を編集し、TORCH_LIBRARY_FRAGMENT(sgl_kernel, m) の内部に追加します。
// From csrc/elementwise
m.def("scale(Tensor! out, Tensor input, float factor) -> ()");
m.impl("scale", torch::kCUDA, &scale);
重要なポイント:
Tensor!はインプレース/変更可能な出力引数を意味します- スキーマは
torch.compileと一貫した呼び出しシグネチャにとって重要です - Torch スキーマでは PyTorch のスカラー型(ここでは
float)を使います。ただし、torch::Libraryで受け取るスカラー引数については、C++ ランチャーのシグネチャでは引き続きdoubleが必要です
ステップ 4:新しいソースファイルを CMakeLists.txt に追加する
python/sglang/kernels/aot/CMakeLists.txt を編集し、set(SOURCES ...) に追加します。
csrc/elementwise/scale.cu
重要なポイント:
- リストはアルファベット順にしてください(ファイルで明示的に求められています)
- カーネルにアーキテクチャの制約がある場合は、スキップロジックを使用してテスト/ベンチマークに反映してください
ステップ 5:python/sglang/kernels/aot/python/sgl_kernel/ に Python API を公開する
まず、既存のモジュール構成に従うことをおすすめします。要素単位のカーネルでは、通常、次のパターンを使用します。
- Python ラッパーを
python/sglang/kernels/aot/python/sgl_kernel/elementwise.pyに実装します - 次に、
python/sglang/kernels/aot/python/sgl_kernel/__init__.pyから再エクスポートします
たとえば、python/sglang/kernels/aot/python/sgl_kernel/elementwise.py に追加します。
import torch
def scale(
input: torch.Tensor,
factor: float,
out: torch.Tensor | None = None,
) -> torch.Tensor:
"""
Element-wise scale: out = input * factor.
Supported dtypes: torch.float16, torch.bfloat16, torch.float32.
Parameters
----------
input : CUDA input tensor
factor : scale factor (float)
out : optional pre-allocated CUDA output tensor (same shape/dtype as input)
"""
if out is None:
out = torch.empty_like(input)
torch.ops.sgl_kernel.scale.default(out, input, factor)
return out
次に、ほかのカーネルで使用されている既存のインポートスタイルに従って、python/sglang/kernels/aot/python/sgl_kernel/__init__.py から再エクスポートします。
SGLang との統合(実行時の使用に必須)
AOT ホイールのシンボルを公開した後、python/sglang/kernels/ops/<group>/ に遅延ラッパーと KernelSpec を追加します。backend=KernelBackend.AOT を設定し、実際にサポートされるデバイス/アーキテクチャを記録してください。また、sgl_kernel のインポートは実装パス内にとどめてください。SGLang の実行時処理と統合テストでは、このラッパーをインポートします。この例では、sglang.kernels.ops.elementwise.scale を使用します。
以下のホイールレベルのテストでは、単独での API/ビルドを検証します。これらは、CI に登録する SGLang の正当性テスト test/registered/kernels/ops/elementwise/test_scale.py やベンチマーク test/registered/kernels/benchmark/elementwise/bench_scale.py の代わりにはなりません。登録方法と CI の実行予算については write-sglang-test に従ってください。配布対象の python/sglang/ パッケージ内にテストを登録しないでください。
ステップ 6:テストを書く(必須)
python/sglang/kernels/aot/tests/test_scale.py を作成します。
import pytest
import torch
import sgl_kernel
@pytest.mark.parametrize("dtype", [torch.float16, torch.bfloat16, torch.float32])
@pytest.mark.parametrize("size", [128, 1024, 4096, 65536])
@pytest.mark.parametrize("factor", [0.5, 1.0, 2.0])
def test_scale_correctness(dtype, size, factor):
input = torch.randn(size, dtype=dtype, device="cuda")
out = torch.empty_like(input)
result = sgl_kernel.scale(input, factor, out=out)
assert result is out
expected = input * factor
rtol, atol = (1e-5, 1e-6) if dtype == torch.float32 else (1e-2, 1e-2)
torch.testing.assert_close(out, expected, rtol=rtol, atol=atol)
def test_scale_shape_mismatch():
input = torch.randn(128, dtype=torch.float16, device="cuda")
out = torch.empty(256, dtype=torch.float16, device="cuda")
with pytest.raises(RuntimeError, match="same shape"):
sgl_kernel.scale(input, 2.0, out=out)
def test_scale_cpu_input():
input = torch.randn(128, dtype=torch.float16) # CPU
out = torch.empty_like(input)
with pytest.raises(RuntimeError, match="CUDA"):
sgl_kernel.scale(input, 2.0, out=out)
if __name__ == "__main__":
import sys
sys.exit(pytest.main([__file__, "-q"]))
ステップ 7:ベンチマークを追加する(必須)
すべてのベンチマークで、L2 キャッシュの再利用を考慮する必要があります。詳しくは rules/kernel-benchmark.md を参照してください。
python/sglang/kernels/aot/benchmark/bench_scale.py を作成します。
import torch
import sgl_kernel
from sglang.kernels.jit.benchmark import marker
def sglang_scale(input: torch.Tensor, factor: float, out: torch.Tensor) -> None:
sgl_kernel.scale(input, factor, out=out)
def torch_scale(input: torch.Tensor, factor: float, out: torch.Tensor) -> None:
torch.mul(input, factor, out=out)
FN_MAP = {"sglang": sglang_scale, "torch": torch_scale}
@marker.parametrize("dtype", [torch.float16, torch.bfloat16, torch.float32], [torch.float16])
@marker.parametrize("size", [2**n for n in range(10, 20)], [4096]) # 1K .. 512K
@marker.benchmark("provider", ["sglang", "torch"])
def benchmark(dtype: torch.dtype, size: int, provider: str):
input = torch.randn(size, dtype=dtype, device="cuda")
out = torch.empty_like(input)
return marker.do_bench(
FN_MAP[provider],
# Pass every tensor through input_args (not a closure) so marker rotates
# them across CUDA-graph calls to defeat L2 reuse.
input_args=(input, 2.0, out),
# Bandwidth = bytes(input) + bytes(out), both already in input_args.
memory_output=None,
)
if __name__ == "__main__":
benchmark.run()
ステップ 8:ビルドする
ビルドします。
cd python/sglang/kernels/aot
make build -j16
ホストのリソース使用量を制限する必要がある場合:
cd python/sglang/kernels/aot
make build -j1 MAX_JOBS=2 CMAKE_ARGS="-DSGL_KERNEL_COMPILE_THREADS=1"
ステップ 9:検証する
ビルドが成功したら、テストとベンチマークを実行します。
pytest python/sglang/kernels/aot/tests/test_scale.py -q
python python/sglang/kernels/aot/benchmark/bench_scale.py
PR の CI では pr-test-sgl-kernel.yml も実行され、カーネルの変更が検出された場合は B200 ジョブ sgl-kernel-b200-test も実行されます。このジョブを、AOT sgl-kernel の変更に対する Blackwell のカバレッジ確認に使用してください。
トラブルシューティング
- 非同期 CUDA エラー:
CUDA_LAUNCH_BLOCKING=1 - メモリエラー:
compute-sanitizer --tool memcheck python ... - ビルドが遅すぎる/OOM が発生する:
MAX_JOBSとSGL_KERNEL_COMPILE_THREADSを減らしてください - バイナリが肥大化する:
python/sglang/kernels/aot/analyze_whl_kernel_sizes.pyを使用してください - CMake のソース一覧:
.cuファイルがSOURCESにない場合、リンク時にシンボルが未定義になります
参考資料
python/sglang/kernels/aot/README.mdpython/sglang/kernels/aot/include/sgl_kernel_ops.hpython/sglang/kernels/aot/csrc/common_extension.ccpython/sglang/kernels/aot/CMakeLists.txtpython/sglang/kernels/aot/include/utils.h—DISPATCH_PYTORCH_DTYPE_TO_CTYPE_FLOAT_FP16マクロなどpython/sglang/kernels/aot/csrc/elementwise/activation.cu— FP16/BF16/FP32 のディスパッチパターンの参考例
作成/変更したファイルの概要
python/sglang/kernels/aot/csrc/elementwise/scale.cu # NEW: CUDA kernel + launcher
python/sglang/kernels/aot/include/sgl_kernel_ops.h # MODIFIED: C++ declaration
python/sglang/kernels/aot/csrc/common_extension.cc # MODIFIED: schema + dispatch registration
python/sglang/kernels/aot/CMakeLists.txt # MODIFIED: add source file (alphabetical)
python/sglang/kernels/aot/python/sgl_kernel/elementwise.py # MODIFIED: Python wrapper
python/sglang/kernels/aot/python/sgl_kernel/__init__.py # MODIFIED: re-export Python API
python/sglang/kernels/aot/tests/test_scale.py # NEW: tests
python/sglang/kernels/aot/benchmark/bench_scale.py # NEW: benchmark