94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
372 lines
12 KiB
Plaintext
372 lines
12 KiB
Plaintext
#include <sgl_kernel/tensor.h>
|
|
#include <sgl_kernel/utils.h>
|
|
|
|
#include <sgl_kernel/runtime.cuh>
|
|
#include <sgl_kernel/tile.cuh>
|
|
#include <sgl_kernel/utils.cuh>
|
|
#include <sgl_kernel/vec.cuh>
|
|
|
|
#include <sgl_kernel/impl/norm.cuh>
|
|
|
|
#include <tvm/ffi/container/tensor.h>
|
|
|
|
namespace {
|
|
|
|
struct RMSNormParams {
|
|
const void* input;
|
|
const void* __restrict__ weight;
|
|
void* output;
|
|
int64_t input_stride;
|
|
int64_t output_stride;
|
|
uint32_t num_tokens;
|
|
float eps;
|
|
};
|
|
|
|
template <int64_t kDim, bool kUsePDL, typename Float>
|
|
__global__ void rmsnorm_cta(const RMSNormParams __grid_constant__ params) {
|
|
using namespace device;
|
|
using Storage = norm::StorageType<Float, kDim>;
|
|
|
|
constexpr auto kNumThreads = host::norm::get_cta_threads<Float, kDim>();
|
|
constexpr auto kNumWarps = kNumThreads / kWarpThreads;
|
|
|
|
const auto& [input, weight_ptr, output, input_stride, output_stride, num_tokens, eps] = params;
|
|
const auto gmem = tile::Memory<Storage>::cta(kNumThreads);
|
|
__shared__ float smem[norm::kSmemBufferSize];
|
|
|
|
PDLWaitPrimary<kUsePDL>(); // wait for primary kernel
|
|
|
|
for (uint32_t i = blockIdx.x; i < num_tokens; i += gridDim.x) {
|
|
const auto input_ptr = pointer::offset<Float>(input, i * input_stride);
|
|
const auto output_ptr = pointer::offset<Float>(output, i * output_stride);
|
|
const auto input_vec = gmem.load(input_ptr);
|
|
const auto weight_vec = gmem.load(weight_ptr);
|
|
const auto output_vec = norm::apply_norm_cta<kDim>(input_vec, weight_vec, eps, smem, kNumWarps);
|
|
gmem.store(output_ptr, output_vec);
|
|
}
|
|
|
|
PDLTriggerSecondary<kUsePDL>(); // launch secondary kernel
|
|
}
|
|
|
|
// Pre-Blackwell: 16B vector, each thread loads/stores twice
|
|
template <int64_t kDim, bool kUsePDL, typename Float>
|
|
__global__ __launch_bounds__(kDim / 16) void rmsnorm_cta_double(const RMSNormParams __grid_constant__ params) {
|
|
using namespace device;
|
|
using Float2 = packed_t<Float>;
|
|
using Storage = AlignedVector<Float2, 4>;
|
|
|
|
constexpr auto kNumThreads = kDim / 16;
|
|
constexpr auto kNumWarps = kNumThreads / kWarpThreads;
|
|
|
|
const auto& [input, weight_ptr, output, input_stride, output_stride, num_tokens, eps] = params;
|
|
const auto gmem = tile::Memory<Storage>::cta(kNumThreads);
|
|
__shared__ float smem[32];
|
|
|
|
PDLWaitPrimary<kUsePDL>();
|
|
|
|
const auto input_ptr = pointer::offset<Float>(input, blockIdx.x * input_stride);
|
|
const auto output_ptr = pointer::offset<Float>(output, blockIdx.x * output_stride);
|
|
|
|
const auto input_first = gmem.load(input_ptr, 0);
|
|
const auto input_second = gmem.load(input_ptr, 1);
|
|
const auto weight_first = gmem.load(weight_ptr, 0);
|
|
const auto weight_second = gmem.load(weight_ptr, 1);
|
|
|
|
float sum_of_squares = 0.0f;
|
|
#pragma unroll
|
|
for (auto j = 0u; j < 4u; ++j) {
|
|
const auto [x, y] = cast<fp32x2_t>(input_first[j]);
|
|
sum_of_squares += x * x + y * y;
|
|
}
|
|
#pragma unroll
|
|
for (auto j = 0u; j < 4u; ++j) {
|
|
const auto [x, y] = cast<fp32x2_t>(input_second[j]);
|
|
sum_of_squares += x * x + y * y;
|
|
}
|
|
|
|
sum_of_squares = warp::reduce_sum(sum_of_squares);
|
|
float norm_factor;
|
|
if constexpr (kNumWarps == 1) {
|
|
norm_factor = math::rsqrt(sum_of_squares / kDim + eps);
|
|
} else {
|
|
const auto warp_id = threadIdx.x / kWarpThreads;
|
|
smem[warp_id] = sum_of_squares;
|
|
__syncthreads();
|
|
if (warp_id == 0) {
|
|
const auto tx = threadIdx.x;
|
|
const auto local_sum = tx < kNumWarps ? smem[tx] : 0.0f;
|
|
sum_of_squares = warp::reduce_sum(local_sum);
|
|
smem[tx] = math::rsqrt(sum_of_squares / kDim + eps);
|
|
}
|
|
__syncthreads();
|
|
norm_factor = smem[warp_id];
|
|
}
|
|
|
|
Storage output_first, output_second;
|
|
#pragma unroll
|
|
for (auto j = 0u; j < 4u; ++j) {
|
|
const auto [ix, iy] = cast<fp32x2_t>(input_first[j]);
|
|
const auto [wx, wy] = cast<fp32x2_t>(weight_first[j]);
|
|
output_first[j] = cast<Float2>(fp32x2_t{ix * norm_factor * wx, iy * norm_factor * wy});
|
|
}
|
|
#pragma unroll
|
|
for (auto j = 0u; j < 4u; ++j) {
|
|
const auto [ix, iy] = cast<fp32x2_t>(input_second[j]);
|
|
const auto [wx, wy] = cast<fp32x2_t>(weight_second[j]);
|
|
output_second[j] = cast<Float2>(fp32x2_t{ix * norm_factor * wx, iy * norm_factor * wy});
|
|
}
|
|
|
|
gmem.store(output_ptr, output_first, 0);
|
|
gmem.store(output_ptr, output_second, 1);
|
|
|
|
PDLTriggerSecondary<kUsePDL>();
|
|
}
|
|
|
|
// Blackwell: 32B vector, each thread loads/stores once
|
|
template <int64_t kDim, bool kUsePDL, typename Float>
|
|
__global__ __launch_bounds__(kDim / 16) void rmsnorm_cta_wide(const RMSNormParams __grid_constant__ params) {
|
|
using namespace device;
|
|
using Float2 = packed_t<Float>;
|
|
using Storage = AlignedVector<Float2, 8>;
|
|
|
|
constexpr auto kNumThreads = kDim / 16;
|
|
constexpr auto kNumWarps = kNumThreads / kWarpThreads;
|
|
|
|
const auto& [input, weight_ptr, output, input_stride, output_stride, num_tokens, eps] = params;
|
|
const auto gmem = tile::Memory<Storage>::cta(kNumThreads);
|
|
__shared__ float smem[32];
|
|
|
|
PDLWaitPrimary<kUsePDL>();
|
|
|
|
const auto input_ptr = pointer::offset<Float>(input, blockIdx.x * input_stride);
|
|
const auto output_ptr = pointer::offset<Float>(output, blockIdx.x * output_stride);
|
|
|
|
const auto input_vec = gmem.load(input_ptr);
|
|
const auto weight_vec = gmem.load(weight_ptr);
|
|
|
|
float sum_of_squares = 0.0f;
|
|
#pragma unroll
|
|
for (auto j = 0u; j < 8u; ++j) {
|
|
const auto [x, y] = cast<fp32x2_t>(input_vec[j]);
|
|
sum_of_squares += x * x + y * y;
|
|
}
|
|
|
|
sum_of_squares = warp::reduce_sum(sum_of_squares);
|
|
float norm_factor;
|
|
if constexpr (kNumWarps == 1) {
|
|
norm_factor = math::rsqrt(sum_of_squares / kDim + eps);
|
|
} else {
|
|
const auto warp_id = threadIdx.x / kWarpThreads;
|
|
smem[warp_id] = sum_of_squares;
|
|
__syncthreads();
|
|
if (warp_id == 0) {
|
|
const auto tx = threadIdx.x;
|
|
const auto local_sum = tx < kNumWarps ? smem[tx] : 0.0f;
|
|
sum_of_squares = warp::reduce_sum(local_sum);
|
|
smem[tx] = math::rsqrt(sum_of_squares / kDim + eps);
|
|
}
|
|
__syncthreads();
|
|
norm_factor = smem[warp_id];
|
|
}
|
|
|
|
Storage output_vec;
|
|
#pragma unroll
|
|
for (auto j = 0u; j < 8u; ++j) {
|
|
const auto [ix, iy] = cast<fp32x2_t>(input_vec[j]);
|
|
const auto [wx, wy] = cast<fp32x2_t>(weight_vec[j]);
|
|
output_vec[j] = cast<Float2>(fp32x2_t{ix * norm_factor * wx, iy * norm_factor * wy});
|
|
}
|
|
|
|
gmem.store(output_ptr, output_vec);
|
|
|
|
PDLTriggerSecondary<kUsePDL>();
|
|
}
|
|
|
|
template <int64_t kDim, bool kUsePDL, typename Float>
|
|
__global__ void rmsnorm_warp(const RMSNormParams __grid_constant__ params) {
|
|
using namespace device;
|
|
using Storage = norm::StorageType<Float, kDim>;
|
|
|
|
const auto& [input, weight_ptr, output, input_stride, output_stride, num_tokens, eps] = params;
|
|
const auto gmem = tile::Memory<Storage>::warp();
|
|
|
|
PDLWaitPrimary<kUsePDL>(); // wait for primary kernel
|
|
|
|
for (uint32_t i = blockIdx.x; i < num_tokens; i += gridDim.x) {
|
|
const auto input_ptr = pointer::offset<Float>(input, i * input_stride);
|
|
const auto output_ptr = pointer::offset<Float>(output, i * output_stride);
|
|
const auto input_vec = gmem.load(input_ptr);
|
|
const auto weight_vec = gmem.load(weight_ptr);
|
|
const auto output_vec = norm::apply_norm_warp<kDim>(input_vec, weight_vec, eps);
|
|
gmem.store(output_ptr, output_vec);
|
|
}
|
|
|
|
PDLTriggerSecondary<kUsePDL>(); // launch secondary kernel
|
|
}
|
|
|
|
template <int64_t kDim, bool kUsePDL, typename DType>
|
|
struct RMSNormWarpKernel {
|
|
static_assert(host::norm::is_config_supported<DType, kDim>(), "Unsupported norm configuration");
|
|
static_assert(kDim <= 256, "Use RMSNormKernel for hidden sizes > 256");
|
|
static constexpr auto kernel = rmsnorm_warp<kDim, kUsePDL, DType>;
|
|
|
|
static void
|
|
run(const tvm::ffi::TensorView input,
|
|
const tvm::ffi::TensorView weight,
|
|
const tvm::ffi::TensorView output,
|
|
float eps) {
|
|
using namespace host;
|
|
auto N = SymbolicSize{"num_tokens"};
|
|
auto D = SymbolicSize{"hidden_size"};
|
|
auto SI = SymbolicSize{"input_stride"};
|
|
auto SO = SymbolicSize{"output_stride"};
|
|
auto device = SymbolicDevice{};
|
|
D.set_value(kDim);
|
|
device.set_options<kDLCUDA>();
|
|
|
|
TensorMatcher({N, D}) // input
|
|
.with_strides({SI, 1})
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(input);
|
|
TensorMatcher({D}) // weight
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(weight);
|
|
TensorMatcher({N, D}) // output
|
|
.with_strides({SO, 1})
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(output);
|
|
|
|
const auto num_tokens = static_cast<uint32_t>(N.unwrap());
|
|
const auto params = RMSNormParams{
|
|
.input = input.data_ptr(),
|
|
.weight = weight.data_ptr(),
|
|
.output = output.data_ptr(),
|
|
.input_stride = SI.unwrap(),
|
|
.output_stride = SO.unwrap(),
|
|
.num_tokens = num_tokens,
|
|
.eps = eps,
|
|
};
|
|
|
|
static constexpr uint32_t kNumThreads = device::kWarpThreads;
|
|
static const uint32_t max_occupancy = runtime::get_blocks_per_sm(kernel, kNumThreads);
|
|
static const uint32_t kNumSM = runtime::get_sm_count(device.unwrap().device_id);
|
|
const auto num_blocks = std::min<uint32_t>(num_tokens, max_occupancy * kNumSM);
|
|
LaunchKernel(num_blocks, kNumThreads, device.unwrap()) //
|
|
.enable_pdl(kUsePDL)(kernel, params);
|
|
}
|
|
};
|
|
|
|
template <int64_t kDim, bool kUsePDL, typename DType>
|
|
struct RMSNormKernel {
|
|
static_assert(host::norm::should_use_cta<DType, kDim>(), "Hidden size invalid for RMSNorm");
|
|
static constexpr auto kernel = rmsnorm_cta<kDim, kUsePDL, DType>;
|
|
|
|
static void
|
|
run(const tvm::ffi::TensorView input,
|
|
const tvm::ffi::TensorView weight,
|
|
const tvm::ffi::TensorView output,
|
|
float eps) {
|
|
using namespace host;
|
|
auto N = SymbolicSize{"num_tokens"};
|
|
auto D = SymbolicSize{"hidden_size"};
|
|
auto SI = SymbolicSize{"input_stride"};
|
|
auto SO = SymbolicSize{"output_stride"};
|
|
auto device = SymbolicDevice{};
|
|
D.set_value(kDim);
|
|
device.set_options<kDLCUDA>();
|
|
|
|
TensorMatcher({N, D}) // input
|
|
.with_strides({SI, 1})
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(input);
|
|
TensorMatcher({D}) // weight
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(weight);
|
|
TensorMatcher({N, D}) // output
|
|
.with_strides({SO, 1})
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(output);
|
|
|
|
const auto num_tokens = static_cast<uint32_t>(N.unwrap());
|
|
const auto params = RMSNormParams{
|
|
.input = input.data_ptr(),
|
|
.weight = weight.data_ptr(),
|
|
.output = output.data_ptr(),
|
|
.input_stride = SI.unwrap(),
|
|
.output_stride = SO.unwrap(),
|
|
.num_tokens = num_tokens,
|
|
.eps = eps,
|
|
};
|
|
|
|
static constexpr auto kNumThreads = norm::get_cta_threads<DType, kDim>();
|
|
static const uint32_t max_occupancy = runtime::get_blocks_per_sm(kernel, kNumThreads);
|
|
static const uint32_t kNumSM = runtime::get_sm_count(device.unwrap().device_id);
|
|
const auto num_blocks = std::min<uint32_t>(num_tokens, max_occupancy * kNumSM);
|
|
LaunchKernel(num_blocks, kNumThreads, device.unwrap()) //
|
|
.enable_pdl(kUsePDL)(kernel, params);
|
|
}
|
|
};
|
|
|
|
template <int64_t kDim, bool kUsePDL, typename DType>
|
|
struct RMSNormHalfKernel {
|
|
static_assert(kDim % 512 == 0 && sizeof(DType) == 2);
|
|
#if SGL_ARCH_BLACKWELL_OR_GREATER
|
|
static constexpr auto kernel = rmsnorm_cta_wide<kDim, kUsePDL, DType>;
|
|
#else
|
|
static constexpr auto kernel = rmsnorm_cta_double<kDim, kUsePDL, DType>;
|
|
#endif
|
|
static constexpr auto kBlockSize = static_cast<uint32_t>(kDim / 16);
|
|
|
|
static void
|
|
run(const tvm::ffi::TensorView input,
|
|
const tvm::ffi::TensorView weight,
|
|
const tvm::ffi::TensorView output,
|
|
float eps) {
|
|
using namespace host;
|
|
auto N = SymbolicSize{"num_tokens"};
|
|
auto D = SymbolicSize{"hidden_size"};
|
|
auto SI = SymbolicSize{"input_stride"};
|
|
auto SO = SymbolicSize{"output_stride"};
|
|
auto device = SymbolicDevice{};
|
|
D.set_value(kDim);
|
|
device.set_options<kDLCUDA>();
|
|
|
|
TensorMatcher({N, D}) // input
|
|
.with_strides({SI, 1})
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(input);
|
|
TensorMatcher({D}) // weight
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(weight);
|
|
TensorMatcher({N, D}) // output
|
|
.with_strides({SO, 1})
|
|
.with_dtype<DType>()
|
|
.with_device(device)
|
|
.verify(output);
|
|
|
|
const auto num_tokens = static_cast<uint32_t>(N.unwrap());
|
|
const auto params = RMSNormParams{
|
|
.input = input.data_ptr(),
|
|
.weight = weight.data_ptr(),
|
|
.output = output.data_ptr(),
|
|
.input_stride = SI.unwrap(),
|
|
.output_stride = SO.unwrap(),
|
|
.num_tokens = num_tokens,
|
|
.eps = eps,
|
|
};
|
|
|
|
LaunchKernel(num_tokens, kBlockSize, device.unwrap()) //
|
|
.enable_pdl(kUsePDL)(kernel, params);
|
|
}
|
|
};
|
|
|
|
} // namespace
|