Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions python/sglang/kernels/aot/benchmark/bench_fp8_gemm.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
import triton
from sgl_kernel import fp8_scaled_mm as sgl_scaled_mm

from sglang.kernels.ops.quantization.per_tensor_quant_fp8 import (
per_tensor_quant_fp8,
)
from sglang.kernels.ops.quantization.per_tensor_quant_fp8 import per_tensor_quant_fp8
from sglang.utils import is_in_ci

# Optional vLLM import
Expand Down Expand Up @@ -106,7 +104,7 @@ def sglang_scaled_fp8_quant(
if IS_CI:
batch_sizes = [1] # Single batch size for CI
else:
batch_sizes = [1, 16, 64, 128, 256, 512, 1024, 2048]
batch_sizes = [1, 2, 8, 16, 64, 128, 256, 512, 1024, 2048]

# Filter line_vals based on vLLM availability
if VLLM_AVAILABLE:
Expand All @@ -115,24 +113,39 @@ def sglang_scaled_fp8_quant(
"vllm-fp8-bf16",
"sglang-fp8-fp16",
"sglang-fp8-bf16",
"sglang-scalar-a-fp8-fp16",
"sglang-scalar-a-fp8-bf16",
]
line_names = [
"vllm-fp8-fp16",
"vllm-fp8-bf16",
"sglang-fp8-fp16",
"sglang-fp8-bf16",
"sglang-scalar-a-fp8-fp16",
"sglang-scalar-a-fp8-bf16",
]
styles = [
("green", "-"),
("green", "--"),
("blue", "-"),
("blue", "--"),
("red", "-"),
("red", "--"),
]
styles = [("green", "-"), ("green", "--"), ("blue", "-"), ("blue", "--")]
else:
line_vals = [
"sglang-fp8-fp16",
"sglang-fp8-bf16",
"sglang-scalar-a-fp8-fp16",
"sglang-scalar-a-fp8-bf16",
]
line_names = [
"sglang-fp8-fp16",
"sglang-fp8-bf16",
"sglang-scalar-a-fp8-fp16",
"sglang-scalar-a-fp8-bf16",
]
styles = [("blue", "-"), ("blue", "--")]
styles = [("blue", "-"), ("blue", "--"), ("red", "-"), ("red", "--")]


@triton.testing.perf_report(
Expand Down Expand Up @@ -174,8 +187,9 @@ def benchmark(batch_size, provider, N, K):
lambda: vllm_scaled_mm(a_fp8, b_fp8, scale_a_fp8, scale_b_fp8, dtype),
quantiles=quantiles,
)
elif "sglang-fp8" in provider:
a_fp8, scale_a_fp8 = sglang_scaled_fp8_quant(a, scale_a)
elif "sglang" in provider:
a_scale = scale_a_scalar if "scalar-a" in provider else scale_a
a_fp8, scale_a_fp8 = sglang_scaled_fp8_quant(a, a_scale)
b_fp8, scale_b_fp8 = sglang_scaled_fp8_quant(b, scale_b)
b_fp8 = b_fp8.t()
ms, min_ms, max_ms = triton.testing.do_bench_cudagraph(
Expand Down
93 changes: 70 additions & 23 deletions python/sglang/kernels/aot/csrc/gemm/fp8_gemm_kernel.cu
Original file line number Diff line number Diff line change
Expand Up @@ -448,19 +448,22 @@ template <
typename MainloopScheduleType,
typename EpilogueScheduleType,
typename TileSchedulerType = void,
bool WithBias = false>
bool WithBias = false,
bool ScalarA = false>
struct DeviceGemmFp8RowwiseSm100 {
static_assert(std::is_same_v<ElementType, cutlass::float_e4m3_t>, "ElementType must be FP8(e4m3)");
using TileShape = CTAShape;
using Accum = cutlass::epilogue::fusion::Sm90AccFetch;

using ElementComputeEpilogue = float;
using ScaleA = cutlass::epilogue::fusion::Sm90ColBroadcast<
using VectorScaleA = cutlass::epilogue::fusion::Sm90ColBroadcast<
0,
TileShape,
ElementComputeEpilogue,
ElementComputeEpilogue,
cute::Stride<cute::Int<1>, cute::Int<0>, cute::Int<0>>>;
using ScalarScaleA = cutlass::epilogue::fusion::Sm90ScalarBroadcast<float>;
using ScaleA = std::conditional_t<ScalarA, ScalarScaleA, VectorScaleA>;

using ScaleB = cutlass::epilogue::fusion::Sm90RowBroadcast<
0,
Expand Down Expand Up @@ -551,7 +554,11 @@ struct DeviceGemmFp8RowwiseSm100 {
auto* data_ptr = static_cast<T*>(tensor.data_ptr());
static_assert(
std::is_same_v<Descriptor, ScaleA> || std::is_same_v<Descriptor, ScaleB> || std::is_same_v<Descriptor, Bias>);
return Arguments{data_ptr};
if constexpr (std::is_same_v<Descriptor, ScalarScaleA>) {
return Arguments{{}, {data_ptr}, {}};
} else {
return Arguments{data_ptr};
}
}

public:
Expand Down Expand Up @@ -657,7 +664,7 @@ void launch_sm100_fp8_scaled_mm(
TORCH_CHECK(status == cutlass::Status::kSuccess)
}

template <typename OutType>
template <typename OutType, bool ScalarA>
void sm100_fp8_dispatch_bias(
torch::Tensor& out,
const torch::Tensor& a,
Expand Down Expand Up @@ -695,7 +702,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
true>;
true,
ScalarA>;
using BiasGemm256 = DeviceGemmFp8RowwiseSm100<
ElementInput,
ElementOutput,
Expand All @@ -705,7 +713,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
true>;
true,
ScalarA>;
using BiasGemm64 = DeviceGemmFp8RowwiseSm100<
ElementInput,
ElementOutput,
Expand All @@ -715,7 +724,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
true>;
true,
ScalarA>;
using BiasGemm16 = DeviceGemmFp8RowwiseSm100<
ElementInput,
ElementOutput,
Expand All @@ -725,7 +735,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
true>;
true,
ScalarA>;

// Gemm type without bias
using GemmDefault = DeviceGemmFp8RowwiseSm100<
Expand All @@ -737,7 +748,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
false>;
false,
ScalarA>;
using Gemm256 = DeviceGemmFp8RowwiseSm100<
ElementInput,
ElementOutput,
Expand All @@ -747,7 +759,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
false>;
false,
ScalarA>;
using Gemm64 = DeviceGemmFp8RowwiseSm100<
ElementInput,
ElementOutput,
Expand All @@ -757,7 +770,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
false>;
false,
ScalarA>;
using Gemm16 = DeviceGemmFp8RowwiseSm100<
ElementInput,
ElementOutput,
Expand All @@ -767,7 +781,8 @@ void sm100_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
false>;
false,
ScalarA>;

// next power of 2 (minimum 16)
uint32_t const m = a.size(0);
Expand Down Expand Up @@ -811,7 +826,10 @@ void sm100_fp8_dispatch_shape(
const torch::Tensor& scales_a,
const torch::Tensor& scales_b,
const c10::optional<torch::Tensor>& bias) {
return sm100_fp8_dispatch_bias<OutType>(out, a, b, scales_a, scales_b, bias);
if (scales_a.numel() == 1) {
return sm100_fp8_dispatch_bias<OutType, true>(out, a, b, scales_a, scales_b, bias);
}
return sm100_fp8_dispatch_bias<OutType, false>(out, a, b, scales_a, scales_b, bias);
}

template <
Expand All @@ -823,19 +841,22 @@ template <
typename MainloopScheduleType,
typename EpilogueScheduleType,
typename TileSchedulerType = void,
bool WithBias = false>
bool WithBias = false,
bool ScalarA = false>
struct DeviceGemmFp8RowwiseSm120 {
static_assert(std::is_same_v<ElementType, cutlass::float_e4m3_t>, "ElementType must be FP8(e4m3)");
using TileShape = CTAShape;
using Accum = cutlass::epilogue::fusion::Sm90AccFetch;

using ElementComputeEpilogue = float;
using ScaleA = cutlass::epilogue::fusion::Sm90ColBroadcast<
using VectorScaleA = cutlass::epilogue::fusion::Sm90ColBroadcast<
0,
TileShape,
ElementComputeEpilogue,
ElementComputeEpilogue,
cute::Stride<cute::Int<1>, cute::Int<0>, cute::Int<0>>>;
using ScalarScaleA = cutlass::epilogue::fusion::Sm90ScalarBroadcast<float>;
using ScaleA = std::conditional_t<ScalarA, ScalarScaleA, VectorScaleA>;

using ScaleB = cutlass::epilogue::fusion::Sm90RowBroadcast<
0,
Expand Down Expand Up @@ -926,7 +947,11 @@ struct DeviceGemmFp8RowwiseSm120 {
auto* data_ptr = static_cast<T*>(tensor.data_ptr());
static_assert(
std::is_same_v<Descriptor, ScaleA> || std::is_same_v<Descriptor, ScaleB> || std::is_same_v<Descriptor, Bias>);
return Arguments{data_ptr};
if constexpr (std::is_same_v<Descriptor, ScalarScaleA>) {
return Arguments{{}, {data_ptr}, {}};
} else {
return Arguments{data_ptr};
}
}

public:
Expand Down Expand Up @@ -1032,7 +1057,7 @@ void launch_sm120_fp8_scaled_mm(
TORCH_CHECK(status == cutlass::Status::kSuccess)
}

template <typename OutType>
template <typename OutType, bool ScalarA>
void sm120_fp8_dispatch_bias(
torch::Tensor& out,
const torch::Tensor& a,
Expand Down Expand Up @@ -1060,7 +1085,8 @@ void sm120_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
true>;
true,
ScalarA>;

using GemmDefault = DeviceGemmFp8RowwiseSm120<
ElementInput,
Expand All @@ -1071,7 +1097,8 @@ void sm120_fp8_dispatch_bias(
MainloopScheduleType,
EpilogueScheduleType,
TileSchedulerType,
false>;
false,
ScalarA>;

if (bias) {
return launch_sm120_fp8_scaled_mm<BiasGemmDefault, true>(out, a, b, scales_a, scales_b, bias);
Expand All @@ -1088,7 +1115,10 @@ void sm120_fp8_dispatch_shape(
const torch::Tensor& scales_a,
const torch::Tensor& scales_b,
const c10::optional<torch::Tensor>& bias) {
return sm120_fp8_dispatch_bias<OutType>(out, a, b, scales_a, scales_b, bias);
if (scales_a.numel() == 1) {
return sm120_fp8_dispatch_bias<OutType, true>(out, a, b, scales_a, scales_b, bias);
}
return sm120_fp8_dispatch_bias<OutType, false>(out, a, b, scales_a, scales_b, bias);
}
#endif

Expand All @@ -1115,7 +1145,26 @@ torch::Tensor fp8_scaled_mm(
TORCH_CHECK(mat_b.scalar_type() == torch::kFloat8_e4m3fn, "mat_b must be Float8_e4m3fn");
TORCH_CHECK(out_dtype == torch::kHalf || out_dtype == torch::kBFloat16, "out_dtype must be Half or BFloat16");

TORCH_CHECK(scales_a.numel() == mat_a.size(0), "size of scales_a is not matched");
auto sm_version = getSMVersion();
TORCH_CHECK(
scales_a.numel() == 1 || scales_a.numel() == mat_a.size(0),
"scales_a must contain either one scalar scale or one scale per row; got ",
scales_a.numel(),
" elements for M=",
mat_a.size(0));
bool scalar_a_scale_supported = false;
#if defined CUDA_VERSION && CUDA_VERSION >= 12000
scalar_a_scale_supported = sm_version == 90;
#endif
#if defined CUDA_VERSION && CUDA_VERSION >= 12080
scalar_a_scale_supported = scalar_a_scale_supported || sm_version >= 100;
#endif
TORCH_CHECK(
scales_a.numel() != 1 || mat_a.size(0) == 1 || scalar_a_scale_supported,
"scalar scales_a with M > 1 is unsupported on SM",
sm_version,
" for this build; got M=",
mat_a.size(0));
TORCH_CHECK(scales_b.numel() == mat_b.size(1), "size of scales_b is not matched");
TORCH_CHECK(scales_a.is_contiguous(), "scales_a must be contiguous");
TORCH_CHECK(scales_b.is_contiguous(), "scales_b msut be contiguous");
Expand All @@ -1131,8 +1180,6 @@ torch::Tensor fp8_scaled_mm(
torch::Tensor out = torch::empty({mat_a.size(0), mat_b.size(1)}, mat_a.options().dtype(out_dtype));
TORCH_CHECK((out.size(1) * out.element_size()) % 16 == 0, "out must be multiple of 16 bytes for memory alignment");

auto sm_version = getSMVersion();

#if defined CUDA_VERSION && CUDA_VERSION >= 12080
if (sm_version >= 120) {
if (out_dtype == torch::kBFloat16) {
Expand Down
Loading
Loading