| 1 | #include <cuda.h> |
| 2 | #include <cuda_runtime.h> |
| 3 | #include <torch/csrc/stable/tensor.h> |
| 4 | #include <torch/headeronly/util/Float8_e4m3fn.h> |
| 5 | |
| 6 | #ifndef USE_ROCM |
| 7 | #include <cuda_fp8.h> |
| 8 | #endif |
| 9 | |
| 10 | #include <cmath> |
| 11 | #include <type_traits> |
| 12 | |
| 13 | #include "../cuda_compat.h" |
| 14 | #include "async_util.cuh" |
| 15 | #include "cuda_vec_utils.cuh" |
| 16 | #include "dispatch_utils.h" |
| 17 | #include "torch_utils.h" |
| 18 | |
| 19 | namespace vllm { |
| 20 | |
| 21 | // `alpha` and `beta` are applied to opposite operands: |
| 22 | // - alpha lives INSIDE the activation (the activated half): the gated |
| 23 | // activation computes act_half * sigmoid(alpha * act_half). |
| 24 | // - beta is added to the OTHER (non-activated) half before the multiply. |
| 25 | // So the result is always ACT(act_half, alpha) * (other_half + beta). |
| 26 | // Which half is which depends on `act_first` (see below). Defaults |
| 27 | // alpha=1.0, beta=0.0 reproduce the plain SwiGLU/GeGLU behavior. |
| 28 | template <typename scalar_t, scalar_t (*ACT_FN)(const scalar_t&, const float), |
| 29 | bool act_first, bool HAS_CLAMP> |
| 30 | __device__ __forceinline__ scalar_t compute(const scalar_t& x, |
| 31 | const scalar_t& y, |
| 32 | const float limit, |
| 33 | const float alpha, |
| 34 | const float beta) { |
| 35 | if constexpr (act_first) { |
| 36 | scalar_t gate = x; |
| 37 | scalar_t up = y; |
| 38 | if constexpr (HAS_CLAMP) { |
| 39 | gate = (scalar_t)fminf((float)gate, limit); |
| 40 | up = (scalar_t)fmaxf(fminf((float)up, limit), -limit); |
| 41 | } |
| 42 | // act_first: gate is the activated half -> alpha applies to gate; |
| 43 | // beta is added to up (the non-activated half). |
| 44 | return (scalar_t)(ACT_FN(gate, alpha) * ((float)up + beta)); |
| 45 | } else { |
| 46 | scalar_t gate = x; |
| 47 | scalar_t up = y; |
| 48 | if constexpr (HAS_CLAMP) { |
| 49 | gate = (scalar_t)fmaxf(fminf((float)gate, limit), -limit); |
| 50 | up = (scalar_t)fminf((float)up, limit); |
| 51 | } |
| 52 | // !act_first: up is the activated half -> alpha applies to up; |
| 53 | // beta is added to gate (the non-activated half). |
| 54 | return (scalar_t)(((float)gate + beta) * ACT_FN(up, alpha)); |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | template <typename packed_t, |
| 59 | packed_t (*PACKED_ACT_FN)(const packed_t&, const float), |
| 60 | bool act_first, bool HAS_CLAMP> |
| 61 | __device__ __forceinline__ packed_t packed_compute(const packed_t& x, |
| 62 | const packed_t& y, |
| 63 | const float limit, |
| 64 | const float alpha, |
| 65 | const float beta) { |
| 66 | if constexpr (act_first) { |
| 67 | packed_t gate = x; |
| 68 | packed_t up = y; |
| 69 | float2 u = cast_to_float2(up); |
| 70 | if constexpr (HAS_CLAMP) { |
| 71 | float2 g = cast_to_float2(gate); |
| 72 | g.x = fminf(g.x, limit); |
| 73 | g.y = fminf(g.y, limit); |
| 74 | u.x = fmaxf(fminf(u.x, limit), -limit); |
| 75 | u.y = fmaxf(fminf(u.y, limit), -limit); |
| 76 | gate = cast_to_packed<packed_t>(g); |
| 77 | } |
| 78 | // act_first: gate is the activated half -> alpha applies to gate; |
| 79 | // beta is added to up (the non-activated half). |
| 80 | float2 activated = cast_to_float2(PACKED_ACT_FN(gate, alpha)); |
| 81 | activated.x *= u.x + beta; |
| 82 | activated.y *= u.y + beta; |
| 83 | return cast_to_packed<packed_t>(activated); |
| 84 | } else { |
| 85 | packed_t gate = x; |
| 86 | packed_t up = y; |
| 87 | float2 g = cast_to_float2(gate); |
| 88 | if constexpr (HAS_CLAMP) { |
| 89 | float2 u = cast_to_float2(up); |
| 90 | g.x = fmaxf(fminf(g.x, limit), -limit); |
| 91 | g.y = fmaxf(fminf(g.y, limit), -limit); |
| 92 | u.x = fminf(u.x, limit); |
| 93 | u.y = fminf(u.y, limit); |
| 94 | up = cast_to_packed<packed_t>(u); |
| 95 | } |
| 96 | // !act_first: up is the activated half -> alpha applies to up; |
| 97 | // beta is added to gate (the non-activated half). |
| 98 | float2 activated = cast_to_float2(PACKED_ACT_FN(up, alpha)); |
| 99 | activated.x *= g.x + beta; |
| 100 | activated.y *= g.y + beta; |
| 101 | return cast_to_packed<packed_t>(activated); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | // Activation and gating kernel template. |
| 106 | template <typename scalar_t, typename packed_t, |
| 107 | scalar_t (*ACT_FN)(const scalar_t&, const float), |
| 108 | packed_t (*PACKED_ACT_FN)(const packed_t&, const float), |
| 109 | bool act_first, bool use_vec, bool HAS_CLAMP, bool use_256b = false> |
| 110 | __global__ void act_and_mul_kernel( |
| 111 | scalar_t* __restrict__ out, // [..., d] |
| 112 | const scalar_t* __restrict__ input, // [..., 2, d] |
| 113 | const int d, const float limit, const float alpha, const float beta) { |
| 114 | const int64_t token_idx = blockIdx.x; |
| 115 | const scalar_t* x_ptr = input + token_idx * 2 * d; |
| 116 | const scalar_t* y_ptr = x_ptr + d; |
| 117 | scalar_t* out_ptr = out + token_idx * d; |
| 118 | |
| 119 | if constexpr (use_vec) { |
| 120 | using cuda_t = typename CUDATypeConverter<scalar_t>::Type; |
| 121 | using pvec_t = PackedVec<cuda_t, use_256b>; |
| 122 | |
| 123 | const pvec_t* x_vec = reinterpret_cast<const pvec_t*>(x_ptr); |
| 124 | const pvec_t* y_vec = reinterpret_cast<const pvec_t*>(y_ptr); |
| 125 | pvec_t* out_vec = reinterpret_cast<pvec_t*>(out_ptr); |
| 126 | const int num_vecs = d / 2 / pvec_t::NUM_ELTS; |
| 127 | |
| 128 | for (int i = threadIdx.x; i < num_vecs; i += blockDim.x) { |
| 129 | pvec_t x, y; |
| 130 | if constexpr (use_256b) { |
| 131 | ld256(x, &x_vec[i]); |
| 132 | ld256(y, &y_vec[i]); |
| 133 | } else { |
| 134 | ld128(x, &x_vec[i]); |
| 135 | ld128(y, &y_vec[i]); |
| 136 | } |
| 137 | #pragma unroll |
| 138 | for (int j = 0; j < pvec_t::NUM_ELTS; j++) { |
| 139 | x.elts[j] = |
| 140 | packed_compute<packed_t, PACKED_ACT_FN, act_first, HAS_CLAMP>( |
| 141 | x.elts[j], y.elts[j], limit, alpha, beta); |
| 142 | } |
| 143 | if constexpr (use_256b) { |
| 144 | st256(x, &out_vec[i]); |
| 145 | } else { |
| 146 | st128(x, &out_vec[i]); |
| 147 | } |
| 148 | } |
| 149 | } else { |
| 150 | // Scalar fallback for unaligned data or small d |
| 151 | for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) { |
| 152 | const scalar_t x = VLLM_LDG(&x_ptr[idx]); |
| 153 | const scalar_t y = VLLM_LDG(&y_ptr[idx]); |
| 154 | out_ptr[idx] = compute<scalar_t, ACT_FN, act_first, HAS_CLAMP>( |
| 155 | x, y, limit, alpha, beta); |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | // Gated activations take an `alpha` argument that scales the sigmoid input |
| 161 | // (`x * sigmoid(alpha * x)`). alpha defaults to 1.0 at all call sites, which |
| 162 | // is exactly SiLU; only the clamp path (silu_and_mul_with_clamp) passes a |
| 163 | // non-default alpha. Activations that do not use alpha simply ignore it. |
| 164 | template <typename T> |
| 165 | __device__ __forceinline__ T silu_kernel(const T& x, const float alpha) { |
| 166 | // x * sigmoid(alpha * x) |
| 167 | return (T)(((float)x) / (1.0f + expf((float)-x * alpha))); |
| 168 | } |
| 169 | |
| 170 | template <typename packed_t> |
| 171 | __device__ __forceinline__ packed_t packed_silu_kernel(const packed_t& val, |
| 172 | const float alpha) { |
| 173 | // x * sigmoid(alpha * x) |
| 174 | float2 fval = cast_to_float2(val); |
| 175 | fval.x = fval.x / (1.0f + expf(-fval.x * alpha)); |
| 176 | fval.y = fval.y / (1.0f + expf(-fval.y * alpha)); |
| 177 | return cast_to_packed<packed_t>(fval); |
| 178 | } |
| 179 | |
| 180 | template <typename T> |
| 181 | __device__ __forceinline__ T gelu_kernel(const T& x, const float /*alpha*/) { |
| 182 | // Equivalent to PyTorch GELU with 'none' approximation. |
| 183 | // Refer to: |
| 184 | // https://github.com/pytorch/pytorch/blob/8ac9b20d4b090c213799e81acf48a55ea8d437d6/aten/src/ATen/native/cuda/ActivationGeluKernel.cu#L36-L38 |
| 185 | const float f = (float)x; |
| 186 | constexpr float ALPHA = M_SQRT1_2; |
| 187 | return (T)(f * 0.5f * (1.0f + ::erf(f * ALPHA))); |
| 188 | } |
| 189 | |
| 190 | template <typename packed_t> |
| 191 | __device__ __forceinline__ packed_t packed_gelu_kernel(const packed_t& val, |
| 192 | const float /*alpha*/) { |
| 193 | // Equivalent to PyTorch GELU with 'none' approximation. |
| 194 | // Refer to: |
| 195 | // https://github.com/pytorch/pytorch/blob/8ac9b20d4b090c213799e81acf48a55ea8d437d6/aten/src/ATen/native/cuda/ActivationGeluKernel.cu#L36-L38 |
| 196 | constexpr float ALPHA = M_SQRT1_2; |
| 197 | float2 fval = cast_to_float2(val); |
| 198 | fval.x = fval.x * 0.5f * (1.0f + ::erf(fval.x * ALPHA)); |
| 199 | fval.y = fval.y * 0.5f * (1.0f + ::erf(fval.y * ALPHA)); |
| 200 | return cast_to_packed<packed_t>(fval); |
| 201 | } |
| 202 | |
| 203 | template <typename T> |
| 204 | __device__ __forceinline__ T gelu_tanh_kernel(const T& x, |
| 205 | const float /*alpha*/) { |
| 206 | // Equivalent to PyTorch GELU with 'tanh' approximation. |
| 207 | // Refer to: |
| 208 | // https://github.com/pytorch/pytorch/blob/8ac9b20d4b090c213799e81acf48a55ea8d437d6/aten/src/ATen/native/cuda/ActivationGeluKernel.cu#L25-L30 |
| 209 | const float f = (float)x; |
| 210 | constexpr float BETA = M_SQRT2 * M_2_SQRTPI * 0.5f; |
| 211 | constexpr float KAPPA = 0.044715; |
| 212 | float x_cube = f * f * f; |
| 213 | float inner = BETA * (f + KAPPA * x_cube); |
| 214 | return (T)(0.5f * f * (1.0f + ::tanhf(inner))); |
| 215 | } |
| 216 | |
| 217 | template <typename packed_t> |
| 218 | __device__ __forceinline__ packed_t |
| 219 | packed_gelu_tanh_kernel(const packed_t& val, const float /*alpha*/) { |
| 220 | // Equivalent to PyTorch GELU with 'tanh' approximation. |
| 221 | // Refer to: |
| 222 | // https://github.com/pytorch/pytorch/blob/8ac9b20d4b090c213799e81acf48a55ea8d437d6/aten/src/ATen/native/cuda/ActivationGeluKernel.cu#L25-L30 |
| 223 | float2 fval = cast_to_float2(val); |
| 224 | constexpr float BETA = M_SQRT2 * M_2_SQRTPI * 0.5f; |
| 225 | constexpr float KAPPA = 0.044715; |
| 226 | |
| 227 | float x_cube = fval.x * fval.x * fval.x; |
| 228 | float inner = BETA * (fval.x + KAPPA * x_cube); |
| 229 | fval.x = 0.5f * fval.x * (1.0f + ::tanhf(inner)); |
| 230 | |
| 231 | x_cube = fval.y * fval.y * fval.y; |
| 232 | inner = BETA * (fval.y + KAPPA * x_cube); |
| 233 | fval.y = 0.5f * fval.y * (1.0f + ::tanhf(inner)); |
| 234 | return cast_to_packed<packed_t>(fval); |
| 235 | } |
| 236 | |
| 237 | } // namespace vllm |
| 238 | |
| 239 | // Launch activation and gating kernel. |
| 240 | // Use ACT_FIRST (bool) indicating whether to apply the activation function |
| 241 | // first. HAS_CLAMP (bool) enables pre-activation clamping: gate input is |
| 242 | // clamped (max only) and up input is clamped (both sides) before the |
| 243 | // activation function is applied. |
| 244 | #define LAUNCH_ACTIVATION_GATE_KERNEL(KERNEL, PACKED_KERNEL, ACT_FIRST, \ |
| 245 | HAS_CLAMP, LIMIT, ALPHA, BETA) \ |
| 246 | auto dtype = input.scalar_type(); \ |
| 247 | int d = input.size(-1) / 2; \ |
| 248 | int64_t num_tokens = input.numel() / input.size(-1); \ |
| 249 | if (num_tokens == 0) { \ |
| 250 | return; \ |
| 251 | } \ |
| 252 | dim3 grid(num_tokens); \ |
| 253 | int cc_major = get_device_prop()->major; \ |
| 254 | int support_vec = \ |
| 255 | (CUDA_VERSION >= 12090 && cc_major >= 10 && num_tokens > 128) \ |
| 256 | ? vllm::VecTraits<true>::ARCH_MAX_VEC_SIZE \ |
| 257 | : vllm::VecTraits<false>::ARCH_MAX_VEC_SIZE; \ |
| 258 | int vec_size = support_vec / input.element_size(); \ |
| 259 | const bool use_vec = (d % vec_size == 0); \ |
| 260 | const torch::stable::accelerator::DeviceGuard device_guard( \ |
| 261 | input.get_device_index()); \ |
| 262 | const cudaStream_t stream = get_current_cuda_stream(); \ |
| 263 | if (use_vec) { \ |
| 264 | dim3 block(std::min(d / vec_size, 1024)); \ |
| 265 | if (CUDA_VERSION >= 12090 && cc_major >= 10 && num_tokens > 128) { \ |
| 266 | VLLM_STABLE_DISPATCH_FLOATING_TYPES(dtype, "act_and_mul_kernel", [&] { \ |
| 267 | vllm::act_and_mul_kernel< \ |
| 268 | scalar_t, typename vllm::PackedTypeConverter<scalar_t>::Type, \ |
| 269 | KERNEL<scalar_t>, \ |
| 270 | PACKED_KERNEL<typename vllm::PackedTypeConverter<scalar_t>::Type>, \ |
| 271 | ACT_FIRST, true, HAS_CLAMP, true><<<grid, block, 0, stream>>>( \ |
| 272 | out.mutable_data_ptr<scalar_t>(), \ |
| 273 | input.const_data_ptr<scalar_t>(), d, LIMIT, ALPHA, BETA); \ |
| 274 | }); \ |
| 275 | } else { \ |
| 276 | VLLM_STABLE_DISPATCH_FLOATING_TYPES(dtype, "act_and_mul_kernel", [&] { \ |
| 277 | vllm::act_and_mul_kernel< \ |
| 278 | scalar_t, typename vllm::PackedTypeConverter<scalar_t>::Type, \ |
| 279 | KERNEL<scalar_t>, \ |
| 280 | PACKED_KERNEL<typename vllm::PackedTypeConverter<scalar_t>::Type>, \ |
| 281 | ACT_FIRST, true, HAS_CLAMP, false><<<grid, block, 0, stream>>>( \ |
| 282 | out.mutable_data_ptr<scalar_t>(), \ |
| 283 | input.const_data_ptr<scalar_t>(), d, LIMIT, ALPHA, BETA); \ |
| 284 | }); \ |
| 285 | } \ |
| 286 | } else { \ |
| 287 | dim3 block(std::min(d, 1024)); \ |
| 288 | VLLM_STABLE_DISPATCH_FLOATING_TYPES(dtype, "act_and_mul_kernel", [&] { \ |
| 289 | vllm::act_and_mul_kernel< \ |
| 290 | scalar_t, typename vllm::PackedTypeConverter<scalar_t>::Type, \ |
| 291 | KERNEL<scalar_t>, \ |
| 292 | PACKED_KERNEL<typename vllm::PackedTypeConverter<scalar_t>::Type>, \ |
| 293 | ACT_FIRST, false, HAS_CLAMP><<<grid, block, 0, stream>>>( \ |
| 294 | out.mutable_data_ptr<scalar_t>(), input.const_data_ptr<scalar_t>(), \ |
| 295 | d, LIMIT, ALPHA, BETA); \ |
| 296 | }); \ |
| 297 | } |
| 298 | |
| 299 | void silu_and_mul(torch::stable::Tensor& out, // [..., d] |
| 300 | torch::stable::Tensor& input) // [..., 2 * d] |
| 301 | { |
| 302 | LAUNCH_ACTIVATION_GATE_KERNEL(vllm::silu_kernel, vllm::packed_silu_kernel, |
| 303 | true, false, 0.0f, 1.0f, 0.0f); |
| 304 | } |
| 305 | |
| 306 | void silu_and_mul_clamp(torch::stable::Tensor& out, // [..., d] |
| 307 | torch::stable::Tensor& input, // [..., 2 * d] |
| 308 | double limit, double alpha, double beta) { |
| 309 | // out = (gate.clamp(max=limit) * sigmoid(alpha * gate.clamp(max=limit))) |
| 310 | // * (up.clamp(+-limit) + beta) |
| 311 | // alpha=1.0, beta=0.0 reduce this to silu(gate) * up. |
| 312 | LAUNCH_ACTIVATION_GATE_KERNEL(vllm::silu_kernel, vllm::packed_silu_kernel, |
| 313 | true, true, (float)limit, (float)alpha, |
| 314 | (float)beta); |
| 315 | } |
| 316 | |
| 317 | void mul_and_silu(torch::stable::Tensor& out, // [..., d] |
| 318 | torch::stable::Tensor& input) // [..., 2 * d] |
| 319 | { |
| 320 | // The difference between mul_and_silu and silu_and_mul is that mul_and_silu |
| 321 | // applies the silu to the latter half of the input. |
| 322 | LAUNCH_ACTIVATION_GATE_KERNEL(vllm::silu_kernel, vllm::packed_silu_kernel, |
| 323 | false, false, 0.0f, 1.0f, 0.0f); |
| 324 | } |
| 325 | |
| 326 | void gelu_and_mul(torch::stable::Tensor& out, // [..., d] |
| 327 | torch::stable::Tensor& input) // [..., 2 * d] |
| 328 | { |
| 329 | LAUNCH_ACTIVATION_GATE_KERNEL(vllm::gelu_kernel, vllm::packed_gelu_kernel, |
| 330 | true, false, 0.0f, 1.0f, 0.0f); |
| 331 | } |
| 332 | |
| 333 | void gelu_tanh_and_mul(torch::stable::Tensor& out, // [..., d] |
| 334 | torch::stable::Tensor& input) // [..., 2 * d] |
| 335 | { |
| 336 | LAUNCH_ACTIVATION_GATE_KERNEL(vllm::gelu_tanh_kernel, |
| 337 | vllm::packed_gelu_tanh_kernel, true, false, |
| 338 | 0.0f, 1.0f, 0.0f); |
| 339 | } |
| 340 | |
| 341 | namespace vllm { |
| 342 | |
| 343 | template <typename T> |
| 344 | __device__ __forceinline__ T fatrelu_kernel(const T& x, const float threshold) { |
| 345 | const float f = (float)x; |
| 346 | return (T)(f > threshold ? f : 0.0f); |
| 347 | } |
| 348 | |
| 349 | template <typename packed_t> |
| 350 | __device__ __forceinline__ packed_t |
| 351 | packed_fatrelu_kernel(const packed_t& val, const float threshold) { |
| 352 | float2 fval = cast_to_float2(val); |
| 353 | fval.x = fval.x > threshold ? fval.x : 0.0f; |
| 354 | fval.y = fval.y > threshold ? fval.y : 0.0f; |
| 355 | return cast_to_packed<packed_t>(fval); |
| 356 | } |
| 357 | |
| 358 | template <typename scalar_t, typename packed_t, |
| 359 | scalar_t (*ACT_FN)(const scalar_t&, const float), |
| 360 | packed_t (*PACKED_ACT_FN)(const packed_t&, const float), bool use_vec, |
| 361 | bool use_256b = false> |
| 362 | __global__ void act_and_mul_kernel_with_param( |
| 363 | scalar_t* __restrict__ out, const scalar_t* __restrict__ input, const int d, |
| 364 | const float param) { |
| 365 | const int64_t token_idx = blockIdx.x; |
| 366 | const scalar_t* x_ptr = input + token_idx * 2 * d; |
| 367 | const scalar_t* y_ptr = x_ptr + d; |
| 368 | scalar_t* out_ptr = out + token_idx * d; |
| 369 | |
| 370 | if constexpr (use_vec) { |
| 371 | using cuda_t = typename CUDATypeConverter<scalar_t>::Type; |
| 372 | using pvec_t = PackedVec<cuda_t, use_256b>; |
| 373 | |
| 374 | const pvec_t* x_vec = reinterpret_cast<const pvec_t*>(x_ptr); |
| 375 | const pvec_t* y_vec = reinterpret_cast<const pvec_t*>(y_ptr); |
| 376 | pvec_t* out_vec = reinterpret_cast<pvec_t*>(out_ptr); |
| 377 | const int num_vecs = d / 2 / pvec_t::NUM_ELTS; |
| 378 | |
| 379 | for (int i = threadIdx.x; i < num_vecs; i += blockDim.x) { |
| 380 | pvec_t x, y; |
| 381 | if constexpr (use_256b) { |
| 382 | ld256(x, &x_vec[i]); |
| 383 | ld256(y, &y_vec[i]); |
| 384 | } else { |
| 385 | ld128(x, &x_vec[i]); |
| 386 | ld128(y, &y_vec[i]); |
| 387 | } |
| 388 | #pragma unroll |
| 389 | for (int j = 0; j < pvec_t::NUM_ELTS; j++) { |
| 390 | x.elts[j] = packed_mul(PACKED_ACT_FN(x.elts[j], param), y.elts[j]); |
| 391 | } |
| 392 | if constexpr (use_256b) { |
| 393 | st256(x, &out_vec[i]); |
| 394 | } else { |
| 395 | st128(x, &out_vec[i]); |
| 396 | } |
| 397 | } |
| 398 | } else { |
| 399 | // Scalar fallback for unaligned data or small d |
| 400 | for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) { |
| 401 | const scalar_t x = VLLM_LDG(&x_ptr[idx]); |
| 402 | const scalar_t y = VLLM_LDG(&y_ptr[idx]); |
| 403 | out_ptr[idx] = ACT_FN(x, param) * y; |
| 404 | } |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | template <typename T> |
| 409 | __device__ __forceinline__ T swigluoai_and_mul(const T& gate, const T& up, |
| 410 | float alpha, float limit) { |
| 411 | // Clamp gate to (-inf, limit] and up to [-limit, limit] |
| 412 | const float g = fminf((float)gate, limit); |
| 413 | const float u = fmaxf(fminf((float)up, limit), -limit); |
| 414 | // glu = gate * sigmoid(gate * alpha), then return (up + 1) * glu |
| 415 | return (T)((u + 1.0f) * g / (1.0f + expf(-g * alpha))); |
| 416 | } |
| 417 | |
| 418 | // Interleaved gate/up: input has [gate0, up0, gate1, up1, ...]. |
| 419 | template <typename scalar_t, |
| 420 | scalar_t (*ACT_FN)(const scalar_t&, const scalar_t&, const float, |
| 421 | const float)> |
| 422 | __global__ void swigluoai_and_mul_kernel( |
| 423 | scalar_t* __restrict__ out, // [..., d] |
| 424 | const scalar_t* __restrict__ input, // [..., 2 * d] (interleaved) |
| 425 | const int d, const float alpha, const float limit) { |
| 426 | // For interleaved data: input has 2*d elements per token (gate/up pairs) |
| 427 | // output has d elements per token |
| 428 | constexpr int VEC_SIZE = 16 / sizeof(scalar_t); |
| 429 | constexpr int PAIRS = VEC_SIZE / 2; // Number of gate/up pairs per int4 load |
| 430 | const int64_t token_idx = blockIdx.x; |
| 431 | const scalar_t* in_ptr = input + token_idx * 2 * d; |
| 432 | scalar_t* out_ptr = out + token_idx * d; |
| 433 | |
| 434 | // Check alignment for 128-bit vectorized access on input. |
| 435 | // For output we use int2 (64-bit) which has 8-byte alignment requirement. |
| 436 | const bool in_aligned = is_16byte_aligned(in_ptr); |
| 437 | const bool out_aligned = |
| 438 | (reinterpret_cast<uintptr_t>(out_ptr) & 7) == 0; // 8-byte for int2 |
| 439 | |
| 440 | if (in_aligned && out_aligned && d >= PAIRS) { |
| 441 | // Fast path: vectorized loop |
| 442 | // Each int4 load gives VEC_SIZE elements = PAIRS gate/up pairs |
| 443 | // Each int2 store writes PAIRS output elements |
| 444 | const int4* in_vec = reinterpret_cast<const int4*>(in_ptr); |
| 445 | int2* out_vec = reinterpret_cast<int2*>(out_ptr); |
| 446 | const int num_vecs = d / PAIRS; |
| 447 | const int vec_end = num_vecs * PAIRS; |
| 448 | |
| 449 | for (int i = threadIdx.x; i < num_vecs; i += blockDim.x) { |
| 450 | int4 v = VLLM_LDG(&in_vec[i]); |
| 451 | int2 r; |
| 452 | auto* vp = reinterpret_cast<scalar_t*>(&v); |
| 453 | auto* rp = reinterpret_cast<scalar_t*>(&r); |
| 454 | #pragma unroll |
| 455 | for (int j = 0; j < PAIRS; j++) { |
| 456 | rp[j] = ACT_FN(vp[2 * j], vp[2 * j + 1], alpha, limit); |
| 457 | } |
| 458 | out_vec[i] = r; |
| 459 | } |
| 460 | // Scalar cleanup for remaining elements |
| 461 | for (int i = vec_end + threadIdx.x; i < d; i += blockDim.x) { |
| 462 | out_ptr[i] = ACT_FN(VLLM_LDG(&in_ptr[2 * i]), |
| 463 | VLLM_LDG(&in_ptr[2 * i + 1]), alpha, limit); |
| 464 | } |
| 465 | } else { |
| 466 | // Scalar fallback for unaligned data or small d |
| 467 | for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) { |
| 468 | // gate = x[..., ::2] (even indices) |
| 469 | const scalar_t gate = VLLM_LDG(&in_ptr[2 * idx]); |
| 470 | // up = x[..., 1::2] (odd indices) |
| 471 | const scalar_t up = VLLM_LDG(&in_ptr[2 * idx + 1]); |
| 472 | out_ptr[idx] = ACT_FN(gate, up, alpha, limit); |
| 473 | } |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | // SITU (Kimi SituGLU) gated activation. Non-interleaved layout: |
| 478 | // input = [gate(d), up(d)] per token. |
| 479 | // gate_out = beta * tanh(gate / beta) * sigmoid(gate) |
| 480 | // up_out = (linear_beta > 0) ? linear_beta * tanh(up / linear_beta) : up |
| 481 | // out = gate_out * up_out |
| 482 | __device__ __forceinline__ float situ_tanh(float x) { return tanhf(x); } |
| 483 | |
| 484 | // Kimi-K3 SITU params; baked into the fused LDG kernel to fold at compile time. |
| 485 | static constexpr float SITU_BETA = 4.0f; |
| 486 | static constexpr float SITU_LINEAR_BETA = 25.0f; |
| 487 | __device__ __forceinline__ float situ_activation(float g, float u, float beta, |
| 488 | float linear_beta, |
| 489 | bool clamp_up, float inv_beta, |
| 490 | float inv_linear_beta) { |
| 491 | // sigmoid(g) == (1 + tanh(g/2)) / 2. |
| 492 | const float gate_out = |
| 493 | (0.5f * beta) * situ_tanh(g * inv_beta) * (1.0f + situ_tanh(g * 0.5f)); |
| 494 | const float up_out = |
| 495 | clamp_up ? linear_beta * situ_tanh(u * inv_linear_beta) : u; |
| 496 | return gate_out * up_out; |
| 497 | } |
| 498 | |
| 499 | template <typename scalar_t> |
| 500 | __global__ void situ_and_mul_kernel( |
| 501 | scalar_t* __restrict__ out, // [..., d] |
| 502 | const scalar_t* __restrict__ input, // [..., 2, d] |
| 503 | const int d, const float beta, const float linear_beta) { |
| 504 | const int64_t row = blockIdx.x; |
| 505 | const scalar_t* gate_ptr = input + row * 2 * d; |
| 506 | const scalar_t* up_ptr = gate_ptr + d; |
| 507 | scalar_t* out_ptr = out + row * d; |
| 508 | const bool clamp_up = linear_beta > 0.0f; |
| 509 | const float inv_beta = 1.0f / beta; |
| 510 | const float inv_linear_beta = clamp_up ? 1.0f / linear_beta : 0.0f; |
| 511 | for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) { |
| 512 | const float g = (float)VLLM_LDG(&gate_ptr[idx]); |
| 513 | const float u = (float)VLLM_LDG(&up_ptr[idx]); |
| 514 | out_ptr[idx] = (scalar_t)situ_activation(g, u, beta, linear_beta, clamp_up, |
| 515 | inv_beta, inv_linear_beta); |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | // Match Humming's hardware FP8 conversion. c10::Float8_e4m3fn's software cast |
| 520 | // can round ties differently from `cvt.rn.satfinite.e4m3.f32`. |
| 521 | __device__ __forceinline__ c10::Float8_e4m3fn cvt_fp8_hw(float x) { |
| 522 | #if !defined(USE_ROCM) && defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800 |
| 523 | return c10::Float8_e4m3fn(__nv_cvt_float_to_fp8(x, __NV_SATFINITE, __NV_E4M3), |
| 524 | c10::Float8_e4m3fn::from_bits()); |
| 525 | #else |
| 526 | return static_cast<c10::Float8_e4m3fn>(x); |
| 527 | #endif |
| 528 | } |
| 529 | |
| 530 | template <typename fp8_type> |
| 531 | __device__ __forceinline__ fp8_type quant_to_fp8(float val, float inv_scale, |
| 532 | float fp8_max) { |
| 533 | float x = val * inv_scale; |
| 534 | x = fmaxf(-fp8_max, fminf(x, fp8_max)); |
| 535 | if constexpr (std::is_same_v<fp8_type, c10::Float8_e4m3fn>) { |
| 536 | return cvt_fp8_hw(x); |
| 537 | } else { |
| 538 | return static_cast<fp8_type>(x); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | __device__ __forceinline__ float warp_reduce_max(float v) { |
| 543 | #pragma unroll |
| 544 | for (int offset = WARP_SIZE / 2; offset > 0; offset >>= 1) { |
| 545 | v = fmaxf(v, VLLM_SHFL_XOR_SYNC(v, offset)); |
| 546 | } |
| 547 | return v; |
| 548 | } |
| 549 | |
| 550 | __device__ __forceinline__ float warp_reduce_absmax(float v) { |
| 551 | return warp_reduce_max(v); |
| 552 | } |
| 553 | |
| 554 | template <typename scalar_t, typename fp8_type, int THREADS, int NUM_STAGES, |
| 555 | int GROUP_SIZE, int GRID_DIM, int D> |
| 556 | __global__ void situ_and_mul_quant_group_pipelined_kernel( |
| 557 | fp8_type* __restrict__ out, float* __restrict__ scale_out, |
| 558 | const scalar_t* __restrict__ input, const int64_t num_rows, |
| 559 | const int32_t* __restrict__ num_valid_tokens_ptr, const int64_t topk) { |
| 560 | const int tid = threadIdx.x; |
| 561 | const int warp_id = tid / WARP_SIZE; |
| 562 | const int lane_id = tid % WARP_SIZE; |
| 563 | static constexpr int NUM_WARPS = THREADS / WARP_SIZE; |
| 564 | static constexpr int NUM_GROUPS = D / GROUP_SIZE; |
| 565 | const int64_t row_bound = |
| 566 | num_valid_tokens_ptr != nullptr |
| 567 | ? max((int64_t)0, |
| 568 | min((int64_t)(*num_valid_tokens_ptr) * topk, num_rows)) |
| 569 | : num_rows; |
| 570 | |
| 571 | static_assert(NUM_GROUPS % 4 == 0, |
| 572 | "float4 scale fill needs NUM_GROUPS % 4 == 0"); |
| 573 | static constexpr int NG4 = NUM_GROUPS / 4; |
| 574 | const int64_t pad_start4 = row_bound * NG4; |
| 575 | const int64_t pad_end4 = num_rows * NG4; |
| 576 | float4* scale4 = reinterpret_cast<float4*>(scale_out); |
| 577 | constexpr float4 ones{1.0f, 1.0f, 1.0f, 1.0f}; |
| 578 | for (int64_t i = pad_start4 + (int64_t)blockIdx.x * THREADS + tid; |
| 579 | i < pad_end4; i += (int64_t)GRID_DIM * THREADS) { |
| 580 | scale4[i] = ones; |
| 581 | } |
| 582 | |
| 583 | if constexpr (sizeof(scalar_t) == 2) { |
| 584 | static constexpr int LD_ELTS = 16 / sizeof(scalar_t); |
| 585 | static constexpr int ELTS_PER_LANE = GROUP_SIZE / WARP_SIZE; |
| 586 | static constexpr int STAGE_ELTS = 2 * GROUP_SIZE; |
| 587 | |
| 588 | extern __shared__ __align__(16) unsigned char smem_raw[]; |
| 589 | scalar_t* warp_smem = reinterpret_cast<scalar_t*>(smem_raw) + |
| 590 | (size_t)warp_id * NUM_STAGES * STAGE_ELTS; |
| 591 | |
| 592 | static constexpr float beta = SITU_BETA; |
| 593 | static constexpr float linear_beta = SITU_LINEAR_BETA; |
| 594 | static constexpr bool clamp_up = linear_beta > 0.0f; |
| 595 | static constexpr float inv_beta = 1.0f / beta; |
| 596 | static constexpr float inv_linear_beta = |
| 597 | clamp_up ? 1.0f / linear_beta : 0.0f; |
| 598 | static constexpr float FP8_MAX = |
| 599 | std::is_same_v<fp8_type, c10::Float8_e4m3fn> ? 448.0f : 224.0f; |
| 600 | static constexpr float inv_fp8_max = 1.0f / FP8_MAX; |
| 601 | |
| 602 | static_assert( |
| 603 | NUM_GROUPS % NUM_WARPS == 0, |
| 604 | "constexpr num_iters requires groups evenly split across warps"); |
| 605 | static constexpr int num_iters = NUM_GROUPS / NUM_WARPS; |
| 606 | |
| 607 | const bool up_half = lane_id >= WARP_SIZE / 2; |
| 608 | const int lane_l = up_half ? lane_id - WARP_SIZE / 2 : lane_id; |
| 609 | const int lane_src_off = (up_half ? D : 0) + lane_l * LD_ELTS; |
| 610 | const int lane_dst_off = (up_half ? GROUP_SIZE : 0) + lane_l * LD_ELTS; |
| 611 | static constexpr int warp_stride = NUM_WARPS * GROUP_SIZE; |
| 612 | |
| 613 | const scalar_t* src_ptr = input + warp_id * GROUP_SIZE + lane_src_off; |
| 614 | static constexpr int src_row_stride = GRID_DIM * 2 * D; |
| 615 | const scalar_t* row_src = src_ptr + blockIdx.x * 2 * D; |
| 616 | |
| 617 | uint32_t* out_ptr = |
| 618 | reinterpret_cast<uint32_t*>(out + warp_id * GROUP_SIZE) + lane_id; |
| 619 | static constexpr int out_row_stride = GRID_DIM * D / 4; |
| 620 | uint32_t* row_out = out_ptr + blockIdx.x * (D / 4); |
| 621 | float* scale_ptr = scale_out + warp_id; |
| 622 | static constexpr int scale_row_stride = GRID_DIM * NUM_GROUPS; |
| 623 | float* row_scale = scale_ptr + blockIdx.x * NUM_GROUPS; |
| 624 | |
| 625 | #pragma unroll 1 |
| 626 | for (int64_t row = blockIdx.x; row < row_bound; row += GRID_DIM, |
| 627 | row_src += src_row_stride, row_out += out_row_stride, |
| 628 | row_scale += scale_row_stride) { |
| 629 | const scalar_t* src = row_src; |
| 630 | |
| 631 | auto issue_load = [&](int it, int slot) { |
| 632 | if (it < num_iters) { |
| 633 | cuda_async::cp_async_shared_global_16_cg( |
| 634 | warp_smem + (size_t)slot * STAGE_ELTS + lane_dst_off, src); |
| 635 | src += warp_stride; |
| 636 | } |
| 637 | cuda_async::cp_async_commit_group(); |
| 638 | }; |
| 639 | |
| 640 | int load_slot = 0; |
| 641 | auto bump = [](int s) { return s + 1 == NUM_STAGES ? 0 : s + 1; }; |
| 642 | #pragma unroll |
| 643 | for (int s = 0; s < NUM_STAGES - 1; s++) { |
| 644 | issue_load(s, load_slot); |
| 645 | load_slot = bump(load_slot); |
| 646 | } |
| 647 | |
| 648 | uint32_t* out_st = row_out; |
| 649 | float* scale_st = row_scale; |
| 650 | |
| 651 | int comp_slot = 0; |
| 652 | for (int it = 0; it < num_iters; it++) { |
| 653 | issue_load(it + NUM_STAGES - 1, load_slot); |
| 654 | load_slot = bump(load_slot); |
| 655 | cuda_async::cp_async_wait_group<NUM_STAGES - 1>(); |
| 656 | |
| 657 | const scalar_t* stage = warp_smem + (size_t)comp_slot * STAGE_ELTS; |
| 658 | comp_slot = bump(comp_slot); |
| 659 | static_assert(ELTS_PER_LANE == 4, |
| 660 | "expects GROUP_SIZE == 4 * WARP_SIZE"); |
| 661 | union V { |
| 662 | float2 f2; |
| 663 | scalar_t s[ELTS_PER_LANE]; |
| 664 | }; |
| 665 | const float2* gate2 = reinterpret_cast<const float2*>(stage); |
| 666 | const float2* up2 = reinterpret_cast<const float2*>(stage + GROUP_SIZE); |
| 667 | const V gv{gate2[lane_id]}; |
| 668 | const V uv{up2[lane_id]}; |
| 669 | const scalar_t* gs = gv.s; |
| 670 | const scalar_t* us = uv.s; |
| 671 | |
| 672 | float acts[ELTS_PER_LANE]; |
| 673 | float thread_max = 0.0f; |
| 674 | #pragma unroll |
| 675 | for (int e = 0; e < ELTS_PER_LANE; e++) { |
| 676 | acts[e] = (float)(scalar_t)situ_activation( |
| 677 | (float)gs[e], (float)us[e], beta, linear_beta, clamp_up, inv_beta, |
| 678 | inv_linear_beta); |
| 679 | thread_max = fmaxf(thread_max, fabsf(acts[e])); |
| 680 | } |
| 681 | const float absmax = fmaxf(warp_reduce_absmax(thread_max), 1e-30f); |
| 682 | const float scale = absmax * inv_fp8_max; |
| 683 | if (lane_id == 0) *scale_st = scale; |
| 684 | scale_st += NUM_WARPS; |
| 685 | const float inv_scale = __fdividef(1.0f, scale); |
| 686 | |
| 687 | union O { |
| 688 | uint32_t u; |
| 689 | fp8_type f[ELTS_PER_LANE]; |
| 690 | }; |
| 691 | O o; |
| 692 | #pragma unroll |
| 693 | for (int e = 0; e < ELTS_PER_LANE; e++) { |
| 694 | o.f[e] = quant_to_fp8<fp8_type>(acts[e], inv_scale, FP8_MAX); |
| 695 | } |
| 696 | out_st[0] = o.u; |
| 697 | out_st += NUM_WARPS * GROUP_SIZE / 4; |
| 698 | } |
| 699 | cuda_async::cp_async_wait_group<0>(); |
| 700 | } |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | // Scalar fallback for the group path (odd d or the fp32 dispatch branch). |
| 705 | template <typename scalar_t, typename fp8_type, int GROUP_SIZE> |
| 706 | __global__ void situ_and_mul_quant_group_scalar_kernel( |
| 707 | fp8_type* __restrict__ out, // [num_tokens, d] |
| 708 | float* __restrict__ scale_out, // [num_tokens, num_groups] |
| 709 | const scalar_t* __restrict__ input, // [num_tokens, 2, d] |
| 710 | const int d, const int num_groups, const float beta, |
| 711 | const float linear_beta, const int64_t num_rows, |
| 712 | const int32_t* __restrict__ num_valid_tokens_ptr, const int64_t topk) { |
| 713 | static constexpr int ELTS_PER_LANE = GROUP_SIZE / WARP_SIZE; |
| 714 | const int tid = threadIdx.x; |
| 715 | const int warp_id = tid / WARP_SIZE; |
| 716 | const int lane_id = tid % WARP_SIZE; |
| 717 | const int num_warps = blockDim.x / WARP_SIZE; |
| 718 | const int64_t row_bound = |
| 719 | num_valid_tokens_ptr != nullptr |
| 720 | ? max((int64_t)0, |
| 721 | min((int64_t)(*num_valid_tokens_ptr) * topk, num_rows)) |
| 722 | : num_rows; |
| 723 | |
| 724 | const bool clamp_up = linear_beta > 0.0f; |
| 725 | const float inv_beta = __fdividef(1.0f, beta); |
| 726 | const float inv_linear_beta = clamp_up ? __fdividef(1.0f, linear_beta) : 0.0f; |
| 727 | static constexpr float FP8_MAX = |
| 728 | std::is_same_v<fp8_type, c10::Float8_e4m3fn> ? 448.0f : 224.0f; |
| 729 | static constexpr float inv_fp8_max = 1.0f / FP8_MAX; |
| 730 | |
| 731 | for (int64_t row = blockIdx.x; row < row_bound; row += gridDim.x) { |
| 732 | const scalar_t* gate_ptr = input + row * 2 * (int64_t)d; |
| 733 | const scalar_t* up_ptr = gate_ptr + d; |
| 734 | fp8_type* out_ptr = out + row * (int64_t)d; |
| 735 | float* scale_row = scale_out + row * (int64_t)num_groups; |
| 736 | |
| 737 | for (int g = warp_id; g < num_groups; g += num_warps) { |
| 738 | const int base = g * GROUP_SIZE; |
| 739 | float acts[ELTS_PER_LANE]; |
| 740 | float thread_max = 0.0f; |
| 741 | #pragma unroll |
| 742 | for (int e = 0; e < ELTS_PER_LANE; e++) { |
| 743 | const int idx = base + e * WARP_SIZE + lane_id; |
| 744 | const float gv = (float)VLLM_LDG(&gate_ptr[idx]); |
| 745 | const float uv = (float)VLLM_LDG(&up_ptr[idx]); |
| 746 | acts[e] = (float)(scalar_t)situ_activation( |
| 747 | gv, uv, beta, linear_beta, clamp_up, inv_beta, inv_linear_beta); |
| 748 | thread_max = fmaxf(thread_max, fabsf(acts[e])); |
| 749 | } |
| 750 | const float absmax = fmaxf(warp_reduce_max(thread_max), 1e-30f); |
| 751 | const float scale = absmax * inv_fp8_max; |
| 752 | if (lane_id == 0) scale_row[g] = scale; |
| 753 | const float inv_scale = __fdividef(1.0f, scale); |
| 754 | #pragma unroll |
| 755 | for (int e = 0; e < ELTS_PER_LANE; e++) { |
| 756 | const int idx = base + e * WARP_SIZE + lane_id; |
| 757 | out_ptr[idx] = quant_to_fp8<fp8_type>(acts[e], inv_scale, FP8_MAX); |
| 758 | } |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | // Fill skipped padding-row scales with 1 so they don't feed NaN into w2. |
| 763 | const int64_t pad_start = row_bound * (int64_t)num_groups; |
| 764 | const int64_t pad_end = num_rows * (int64_t)num_groups; |
| 765 | for (int64_t i = pad_start + (int64_t)blockIdx.x * blockDim.x + tid; |
| 766 | i < pad_end; i += (int64_t)gridDim.x * blockDim.x) { |
| 767 | scale_out[i] = 1.0f; |
| 768 | } |
| 769 | } |
| 770 | |
| 771 | constexpr int kMaxMaskedTokenBlocks = 32; |
| 772 | |
| 773 | template <bool BATCHED_EXPERTS> |
| 774 | __device__ __forceinline__ bool get_masked_row_range( |
| 775 | const int* __restrict__ valid_token_counts, const int max_num_tokens, |
| 776 | int64_t& first_row, int64_t& end_row, int64_t& row_stride) { |
| 777 | if constexpr (BATCHED_EXPERTS) { |
| 778 | // [E, T, *]: z lanes grid-stride over one expert's valid token prefix. |
| 779 | const int expert = blockIdx.y; |
| 780 | const int num_tokens = |
| 781 | max(0, min(valid_token_counts[expert], max_num_tokens)); |
| 782 | const int token_block = blockIdx.z; |
| 783 | if (token_block >= num_tokens) { |
| 784 | return false; |
| 785 | } |
| 786 | const int64_t expert_first_row = |
| 787 | static_cast<int64_t>(expert) * max_num_tokens; |
| 788 | first_row = expert_first_row + token_block; |
| 789 | end_row = expert_first_row + num_tokens; |
| 790 | row_stride = gridDim.z; |
| 791 | } else { |
| 792 | // [T, *]: y lanes grid-stride over the single valid token prefix. |
| 793 | const int num_tokens = max(0, min(valid_token_counts[0], max_num_tokens)); |
| 794 | const int token_block = blockIdx.y; |
| 795 | if (token_block >= num_tokens) { |
| 796 | return false; |
| 797 | } |
| 798 | first_row = token_block; |
| 799 | end_row = num_tokens; |
| 800 | row_stride = gridDim.y; |
| 801 | } |
| 802 | return first_row < end_row; |
| 803 | } |
| 804 | |
| 805 | __device__ __forceinline__ int get_masked_feature_index() { |
| 806 | return blockIdx.x * blockDim.x + threadIdx.x; |
| 807 | } |
| 808 | |
| 809 | template <typename scalar_t, bool BATCHED_EXPERTS> |
| 810 | __global__ void masked_situ_and_mul_kernel( |
| 811 | scalar_t* __restrict__ out, const scalar_t* __restrict__ input, |
| 812 | const int* __restrict__ valid_token_counts, const int max_num_tokens, |
| 813 | const int d, const float beta, const float linear_beta) { |
| 814 | int64_t first_row, end_row, row_stride; |
| 815 | const int idx = get_masked_feature_index(); |
| 816 | if (idx >= d || |
| 817 | !get_masked_row_range<BATCHED_EXPERTS>(valid_token_counts, max_num_tokens, |
| 818 | first_row, end_row, row_stride)) { |
| 819 | return; |
| 820 | } |
| 821 | |
| 822 | const bool clamp_up = linear_beta > 0.0f; |
| 823 | const float inv_beta = 1.0f / beta; |
| 824 | const float inv_linear_beta = clamp_up ? 1.0f / linear_beta : 0.0f; |
| 825 | for (int64_t row = first_row; row < end_row; row += row_stride) { |
| 826 | const scalar_t* gate_ptr = input + row * 2 * d; |
| 827 | const scalar_t* up_ptr = gate_ptr + d; |
| 828 | scalar_t* out_ptr = out + row * d; |
| 829 | const float g = (float)VLLM_LDG(&gate_ptr[idx]); |
| 830 | const float u = (float)VLLM_LDG(&up_ptr[idx]); |
| 831 | out_ptr[idx] = (scalar_t)situ_activation(g, u, beta, linear_beta, clamp_up, |
| 832 | inv_beta, inv_linear_beta); |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | template <typename scalar_t, scalar_t (*ACT_FN)(const scalar_t&, const float), |
| 837 | bool act_first, bool HAS_CLAMP, bool BATCHED_EXPERTS> |
| 838 | __global__ void masked_act_and_mul_kernel( |
| 839 | scalar_t* __restrict__ out, const scalar_t* __restrict__ input, |
| 840 | const int* __restrict__ valid_token_counts, const int max_num_tokens, |
| 841 | const int d, const float limit, const float alpha, const float beta) { |
| 842 | int64_t first_row, end_row, row_stride; |
| 843 | const int idx = get_masked_feature_index(); |
| 844 | if (idx >= d || |
| 845 | !get_masked_row_range<BATCHED_EXPERTS>(valid_token_counts, max_num_tokens, |
| 846 | first_row, end_row, row_stride)) { |
| 847 | return; |
| 848 | } |
| 849 | |
| 850 | for (int64_t row = first_row; row < end_row; row += row_stride) { |
| 851 | const scalar_t* x_ptr = input + row * 2 * d; |
| 852 | const scalar_t* y_ptr = x_ptr + d; |
| 853 | scalar_t* out_ptr = out + row * d; |
| 854 | const scalar_t x = VLLM_LDG(&x_ptr[idx]); |
| 855 | const scalar_t y = VLLM_LDG(&y_ptr[idx]); |
| 856 | out_ptr[idx] = compute<scalar_t, ACT_FN, act_first, HAS_CLAMP>(x, y, limit, |
| 857 | alpha, beta); |
| 858 | } |
| 859 | } |
| 860 | |
| 861 | template <typename scalar_t, bool BATCHED_EXPERTS> |
| 862 | __global__ void masked_swigluoai_and_mul_kernel( |
| 863 | scalar_t* __restrict__ out, const scalar_t* __restrict__ input, |
| 864 | const int* __restrict__ valid_token_counts, const int max_num_tokens, |
| 865 | const int d, const float alpha, const float limit) { |
| 866 | int64_t first_row, end_row, row_stride; |
| 867 | const int idx = get_masked_feature_index(); |
| 868 | if (idx >= d || |
| 869 | !get_masked_row_range<BATCHED_EXPERTS>(valid_token_counts, max_num_tokens, |
| 870 | first_row, end_row, row_stride)) { |
| 871 | return; |
| 872 | } |
| 873 | |
| 874 | for (int64_t row = first_row; row < end_row; row += row_stride) { |
| 875 | const scalar_t* in_ptr = input + row * 2 * d; |
| 876 | scalar_t* out_ptr = out + row * d; |
| 877 | out_ptr[idx] = |
| 878 | swigluoai_and_mul(in_ptr[2 * idx], in_ptr[2 * idx + 1], alpha, limit); |
| 879 | } |
| 880 | } |
| 881 | |
| 882 | template <typename scalar_t, bool BATCHED_EXPERTS> |
| 883 | __global__ void masked_swiglustep_and_mul_kernel( |
| 884 | scalar_t* __restrict__ out, const scalar_t* __restrict__ input, |
| 885 | const int* __restrict__ valid_token_counts, const int max_num_tokens, |
| 886 | const int d, const float limit) { |
| 887 | int64_t first_row, end_row, row_stride; |
| 888 | const int idx = get_masked_feature_index(); |
| 889 | if (idx >= d || |
| 890 | !get_masked_row_range<BATCHED_EXPERTS>(valid_token_counts, max_num_tokens, |
| 891 | first_row, end_row, row_stride)) { |
| 892 | return; |
| 893 | } |
| 894 | |
| 895 | for (int64_t row = first_row; row < end_row; row += row_stride) { |
| 896 | const scalar_t* gate_ptr = input + row * 2 * d; |
| 897 | const scalar_t* up_ptr = gate_ptr + d; |
| 898 | scalar_t* out_ptr = out + row * d; |
| 899 | const float gate = (float)VLLM_LDG(&gate_ptr[idx]); |
| 900 | const float up = (float)VLLM_LDG(&up_ptr[idx]); |
| 901 | const float gate_silu = gate / (1.0f + expf(-gate)); |
| 902 | const float gate_clamped = fminf(gate_silu, limit); |
| 903 | const float up_clamped = fmaxf(fminf(up, limit), -limit); |
| 904 | out_ptr[idx] = (scalar_t)(gate_clamped * up_clamped); |
| 905 | } |
| 906 | } |
| 907 | |
| 908 | template <typename T> |
| 909 | __device__ __forceinline__ T silu_no_mul_kernel(const T& x) { |
| 910 | return silu_kernel(x, 1.0f); |
| 911 | } |
| 912 | |
| 913 | template <typename T> |
| 914 | __device__ __forceinline__ T gelu_no_mul_kernel(const T& x) { |
| 915 | return gelu_kernel(x, 1.0f); |
| 916 | } |
| 917 | |
| 918 | template <typename T> |
| 919 | __device__ __forceinline__ T gelu_tanh_no_mul_kernel(const T& x) { |
| 920 | return gelu_tanh_kernel(x, 1.0f); |
| 921 | } |
| 922 | |
| 923 | template <typename T> |
| 924 | __device__ __forceinline__ T relu2_no_mul_kernel(const T& x) { |
| 925 | const float value = fmaxf((float)x, 0.0f); |
| 926 | return (T)(value * value); |
| 927 | } |
| 928 | |
| 929 | template <typename scalar_t, scalar_t (*ACT_FN)(const scalar_t&), |
| 930 | bool BATCHED_EXPERTS> |
| 931 | __global__ void masked_activation_kernel( |
| 932 | scalar_t* __restrict__ out, const scalar_t* __restrict__ input, |
| 933 | const int* __restrict__ valid_token_counts, const int max_num_tokens, |
| 934 | const int d) { |
| 935 | int64_t first_row, end_row, row_stride; |
| 936 | const int idx = get_masked_feature_index(); |
| 937 | if (idx >= d || |
| 938 | !get_masked_row_range<BATCHED_EXPERTS>(valid_token_counts, max_num_tokens, |
| 939 | first_row, end_row, row_stride)) { |
| 940 | return; |
| 941 | } |
| 942 | |
| 943 | for (int64_t row = first_row; row < end_row; row += row_stride) { |
| 944 | const int64_t offset = row * d + idx; |
| 945 | out[offset] = ACT_FN(VLLM_LDG(&input[offset])); |
| 946 | } |
| 947 | } |
| 948 | |
| 949 | } // namespace vllm |
| 950 | |
| 951 | #define LAUNCH_ACTIVATION_GATE_KERNEL_WITH_PARAM(KERNEL, PACKED_KERNEL, PARAM) \ |
| 952 | auto dtype = input.scalar_type(); \ |
| 953 | int d = input.size(-1) / 2; \ |
| 954 | int64_t num_tokens = input.numel() / input.size(-1); \ |
| 955 | if (num_tokens == 0) { \ |
| 956 | return; \ |
| 957 | } \ |
| 958 | dim3 grid(num_tokens); \ |
| 959 | int cc_major = get_device_prop()->major; \ |
| 960 | int support_vec = \ |
| 961 | (CUDA_VERSION >= 12090 && cc_major >= 10 && num_tokens > 128) \ |
| 962 | ? vllm::VecTraits<true>::ARCH_MAX_VEC_SIZE \ |
| 963 | : vllm::VecTraits<false>::ARCH_MAX_VEC_SIZE; \ |
| 964 | int vec_size = support_vec / input.element_size(); \ |
| 965 | const bool use_vec = (d % vec_size == 0); \ |
| 966 | const torch::stable::accelerator::DeviceGuard device_guard( \ |
| 967 | input.get_device_index()); \ |
| 968 | const cudaStream_t stream = get_current_cuda_stream(); \ |
| 969 | if (use_vec) { \ |
| 970 | dim3 block(std::min(d / vec_size, 1024)); \ |
| 971 | if (CUDA_VERSION >= 12090 && cc_major >= 10 && num_tokens > 128) { \ |
| 972 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( \ |
| 973 | dtype, "act_and_mul_kernel_with_param", [&] { \ |
| 974 | vllm::act_and_mul_kernel_with_param< \ |
| 975 | scalar_t, typename vllm::PackedTypeConverter<scalar_t>::Type, \ |
| 976 | KERNEL<scalar_t>, \ |
| 977 | PACKED_KERNEL< \ |
| 978 | typename vllm::PackedTypeConverter<scalar_t>::Type>, \ |
| 979 | true, true><<<grid, block, 0, stream>>>( \ |
| 980 | out.mutable_data_ptr<scalar_t>(), \ |
| 981 | input.const_data_ptr<scalar_t>(), d, PARAM); \ |
| 982 | }); \ |
| 983 | } else { \ |
| 984 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( \ |
| 985 | dtype, "act_and_mul_kernel_with_param", [&] { \ |
| 986 | vllm::act_and_mul_kernel_with_param< \ |
| 987 | scalar_t, typename vllm::PackedTypeConverter<scalar_t>::Type, \ |
| 988 | KERNEL<scalar_t>, \ |
| 989 | PACKED_KERNEL< \ |
| 990 | typename vllm::PackedTypeConverter<scalar_t>::Type>, \ |
| 991 | true, false><<<grid, block, 0, stream>>>( \ |
| 992 | out.mutable_data_ptr<scalar_t>(), \ |
| 993 | input.const_data_ptr<scalar_t>(), d, PARAM); \ |
| 994 | }); \ |
| 995 | } \ |
| 996 | } else { \ |
| 997 | dim3 block(std::min(d, 1024)); \ |
| 998 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( \ |
| 999 | dtype, "act_and_mul_kernel_with_param", [&] { \ |
| 1000 | vllm::act_and_mul_kernel_with_param< \ |
| 1001 | scalar_t, typename vllm::PackedTypeConverter<scalar_t>::Type, \ |
| 1002 | KERNEL<scalar_t>, \ |
| 1003 | PACKED_KERNEL< \ |
| 1004 | typename vllm::PackedTypeConverter<scalar_t>::Type>, \ |
| 1005 | false><<<grid, block, 0, stream>>>( \ |
| 1006 | out.mutable_data_ptr<scalar_t>(), \ |
| 1007 | input.const_data_ptr<scalar_t>(), d, PARAM); \ |
| 1008 | }); \ |
| 1009 | } |
| 1010 | |
| 1011 | #define LAUNCH_SIGLUOAI_AND_MUL(KERNEL, ALPHA, LIMIT) \ |
| 1012 | int d = input.size(-1) / 2; \ |
| 1013 | int64_t num_tokens = input.numel() / input.size(-1); \ |
| 1014 | dim3 grid(num_tokens); \ |
| 1015 | dim3 block(std::min(d, 1024)); \ |
| 1016 | const torch::stable::accelerator::DeviceGuard device_guard( \ |
| 1017 | input.get_device_index()); \ |
| 1018 | const cudaStream_t stream = get_current_cuda_stream(); \ |
| 1019 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( \ |
| 1020 | input.scalar_type(), "clamp_swiglu_kernel_with_params", [&] { \ |
| 1021 | vllm::swigluoai_and_mul_kernel<scalar_t, KERNEL<scalar_t>> \ |
| 1022 | <<<grid, block, 0, stream>>>(out.mutable_data_ptr<scalar_t>(), \ |
| 1023 | input.const_data_ptr<scalar_t>(), d, \ |
| 1024 | ALPHA, LIMIT); \ |
| 1025 | }); |
| 1026 | |
| 1027 | void fatrelu_and_mul(torch::stable::Tensor& out, // [..., d], |
| 1028 | torch::stable::Tensor& input, // [..., 2 * d] |
| 1029 | double threshold) { |
| 1030 | LAUNCH_ACTIVATION_GATE_KERNEL_WITH_PARAM( |
| 1031 | vllm::fatrelu_kernel, vllm::packed_fatrelu_kernel, threshold); |
| 1032 | } |
| 1033 | void swigluoai_and_mul(torch::stable::Tensor& out, // [..., d] |
| 1034 | torch::stable::Tensor& input, // [..., 2 * d] |
| 1035 | double alpha, double limit) { |
| 1036 | LAUNCH_SIGLUOAI_AND_MUL(vllm::swigluoai_and_mul, alpha, limit); |
| 1037 | } |
| 1038 | |
| 1039 | // Kimi SITU gated activation. `linear_beta <= 0` means "unset" (up passed |
| 1040 | // through), matching SituAndMul(linear_beta=None) on the Python side. |
| 1041 | void situ_and_mul(torch::stable::Tensor& out, // [..., d] |
| 1042 | torch::stable::Tensor& input, // [..., 2 * d] |
| 1043 | double beta, double linear_beta) { |
| 1044 | int d = input.size(-1) / 2; |
| 1045 | int64_t num_tokens = input.numel() / input.size(-1); |
| 1046 | if (num_tokens == 0) { |
| 1047 | return; |
| 1048 | } |
| 1049 | dim3 grid(num_tokens); |
| 1050 | dim3 block(std::min(d, 1024)); |
| 1051 | const torch::stable::accelerator::DeviceGuard device_guard( |
| 1052 | input.get_device_index()); |
| 1053 | const cudaStream_t stream = get_current_cuda_stream(); |
| 1054 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( |
| 1055 | input.scalar_type(), "situ_and_mul_kernel", [&] { |
| 1056 | vllm::situ_and_mul_kernel<scalar_t><<<grid, block, 0, stream>>>( |
| 1057 | out.mutable_data_ptr<scalar_t>(), input.const_data_ptr<scalar_t>(), |
| 1058 | d, (float)beta, (float)linear_beta); |
| 1059 | }); |
| 1060 | } |
| 1061 | |
| 1062 | void masked_situ_and_mul(torch::stable::Tensor& out, // [E, T, d] |
| 1063 | torch::stable::Tensor& input, // [E, T, 2 * d] |
| 1064 | const torch::stable::Tensor& expert_num_tokens, |
| 1065 | double beta, double linear_beta) { |
| 1066 | int num_experts = input.size(0); |
| 1067 | int max_num_tokens = input.size(1); |
| 1068 | int d = input.size(2) / 2; |
| 1069 | if (num_experts == 0 || max_num_tokens == 0 || d == 0) { |
| 1070 | return; |
| 1071 | } |
| 1072 | constexpr int block_size = 256; |
| 1073 | dim3 grid((d + block_size - 1) / block_size, num_experts); |
| 1074 | dim3 block(block_size); |
| 1075 | const torch::stable::accelerator::DeviceGuard device_guard( |
| 1076 | input.get_device_index()); |
| 1077 | const cudaStream_t stream = get_current_cuda_stream(); |
| 1078 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( |
| 1079 | input.scalar_type(), "masked_situ_and_mul_kernel", [&] { |
| 1080 | vllm::masked_situ_and_mul_kernel<scalar_t, true> |
| 1081 | <<<grid, block, 0, stream>>>( |
| 1082 | out.mutable_data_ptr<scalar_t>(), |
| 1083 | input.const_data_ptr<scalar_t>(), |
| 1084 | expert_num_tokens.const_data_ptr<int>(), max_num_tokens, d, |
| 1085 | (float)beta, (float)linear_beta); |
| 1086 | }); |
| 1087 | } |
| 1088 | |
| 1089 | #define LAUNCH_MASKED_ACT_AND_MUL(KERNEL, ACT_FIRST, HAS_CLAMP, LIMIT, ALPHA, \ |
| 1090 | BETA) \ |
| 1091 | vllm::masked_act_and_mul_kernel<scalar_t, KERNEL<scalar_t>, ACT_FIRST, \ |
| 1092 | HAS_CLAMP, BATCHED_EXPERTS> \ |
| 1093 | <<<grid, block, 0, stream>>>(out.mutable_data_ptr<scalar_t>(), \ |
| 1094 | input.const_data_ptr<scalar_t>(), \ |
| 1095 | valid_token_counts.const_data_ptr<int>(), \ |
| 1096 | max_num_tokens, d, LIMIT, ALPHA, BETA) |
| 1097 | |
| 1098 | #define LAUNCH_MASKED_ACTIVATION(KERNEL) \ |
| 1099 | vllm::masked_activation_kernel<scalar_t, KERNEL<scalar_t>, BATCHED_EXPERTS> \ |
| 1100 | <<<grid, block, 0, stream>>>( \ |
| 1101 | out.mutable_data_ptr<scalar_t>(), input.const_data_ptr<scalar_t>(), \ |
| 1102 | valid_token_counts.const_data_ptr<int>(), max_num_tokens, d) |
| 1103 | |
| 1104 | void masked_moe_activation( |
| 1105 | torch::stable::Tensor& out, // [T, d] or [E, T, d] |
| 1106 | torch::stable::Tensor& input, // [T, d] or [E, T, d], gated has 2 * d |
| 1107 | const torch::stable::Tensor& valid_token_counts, |
| 1108 | const std::string& activation, double clamp_limit, double alpha, |
| 1109 | double beta, double situ_beta, double situ_linear_beta) { |
| 1110 | const bool batched_experts = input.dim() == 3; |
| 1111 | const int num_experts = batched_experts ? input.size(0) : 1; |
| 1112 | const int max_num_tokens = batched_experts ? input.size(1) : input.size(0); |
| 1113 | const int d = out.size(-1); |
| 1114 | if (num_experts == 0 || max_num_tokens == 0 || d == 0) { |
| 1115 | return; |
| 1116 | } |
| 1117 | |
| 1118 | constexpr int block_size = 256; |
| 1119 | const int feature_blocks = (d + block_size - 1) / block_size; |
| 1120 | const int token_blocks = |
| 1121 | std::min(max_num_tokens, vllm::kMaxMaskedTokenBlocks); |
| 1122 | // Batched grid: (feature tile, expert, token lane); flat grid: (feature |
| 1123 | // tile, token lane). Token lanes grid-stride each valid prefix. |
| 1124 | dim3 grid = batched_experts ? dim3(feature_blocks, num_experts, token_blocks) |
| 1125 | : dim3(feature_blocks, token_blocks); |
| 1126 | dim3 block(block_size); |
| 1127 | const torch::stable::accelerator::DeviceGuard device_guard( |
| 1128 | input.get_device_index()); |
| 1129 | const cudaStream_t stream = get_current_cuda_stream(); |
| 1130 | auto launch = [&]<bool BATCHED_EXPERTS>() { |
| 1131 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( |
| 1132 | input.scalar_type(), "masked_moe_activation", [&] { |
| 1133 | if (activation == "silu") { |
| 1134 | LAUNCH_MASKED_ACT_AND_MUL(vllm::silu_kernel, true, false, 0.0f, |
| 1135 | 1.0f, 0.0f); |
| 1136 | } else if (activation == "silu_with_clamp") { |
| 1137 | LAUNCH_MASKED_ACT_AND_MUL(vllm::silu_kernel, true, true, |
| 1138 | (float)clamp_limit, (float)alpha, |
| 1139 | (float)beta); |
| 1140 | } else if (activation == "gelu") { |
| 1141 | LAUNCH_MASKED_ACT_AND_MUL(vllm::gelu_kernel, true, false, 0.0f, |
| 1142 | 1.0f, 0.0f); |
| 1143 | } else if (activation == "gelu_tanh") { |
| 1144 | LAUNCH_MASKED_ACT_AND_MUL(vllm::gelu_tanh_kernel, true, false, 0.0f, |
| 1145 | 1.0f, 0.0f); |
| 1146 | } else if (activation == "situ") { |
| 1147 | vllm::masked_situ_and_mul_kernel<scalar_t, BATCHED_EXPERTS> |
| 1148 | <<<grid, block, 0, stream>>>( |
| 1149 | out.mutable_data_ptr<scalar_t>(), |
| 1150 | input.const_data_ptr<scalar_t>(), |
| 1151 | valid_token_counts.const_data_ptr<int>(), max_num_tokens, d, |
| 1152 | (float)situ_beta, (float)situ_linear_beta); |
| 1153 | } else if (activation == "swigluoai") { |
| 1154 | vllm::masked_swigluoai_and_mul_kernel<scalar_t, BATCHED_EXPERTS> |
| 1155 | <<<grid, block, 0, stream>>>( |
| 1156 | out.mutable_data_ptr<scalar_t>(), |
| 1157 | input.const_data_ptr<scalar_t>(), |
| 1158 | valid_token_counts.const_data_ptr<int>(), max_num_tokens, d, |
| 1159 | (float)alpha, (float)clamp_limit); |
| 1160 | } else if (activation == "swigluoai_uninterleave") { |
| 1161 | LAUNCH_MASKED_ACT_AND_MUL(vllm::silu_kernel, true, true, |
| 1162 | (float)clamp_limit, (float)alpha, |
| 1163 | (float)beta); |
| 1164 | } else if (activation == "swiglustep") { |
| 1165 | vllm::masked_swiglustep_and_mul_kernel<scalar_t, BATCHED_EXPERTS> |
| 1166 | <<<grid, block, 0, stream>>>( |
| 1167 | out.mutable_data_ptr<scalar_t>(), |
| 1168 | input.const_data_ptr<scalar_t>(), |
| 1169 | valid_token_counts.const_data_ptr<int>(), max_num_tokens, d, |
| 1170 | (float)clamp_limit); |
| 1171 | } else if (activation == "silu_no_mul") { |
| 1172 | LAUNCH_MASKED_ACTIVATION(vllm::silu_no_mul_kernel); |
| 1173 | } else if (activation == "gelu_no_mul") { |
| 1174 | LAUNCH_MASKED_ACTIVATION(vllm::gelu_no_mul_kernel); |
| 1175 | } else if (activation == "gelu_tanh_no_mul") { |
| 1176 | LAUNCH_MASKED_ACTIVATION(vllm::gelu_tanh_no_mul_kernel); |
| 1177 | } else if (activation == "relu2_no_mul") { |
| 1178 | LAUNCH_MASKED_ACTIVATION(vllm::relu2_no_mul_kernel); |
| 1179 | } else { |
| 1180 | STD_TORCH_CHECK(false, |
| 1181 | "Unsupported masked MoE activation: ", activation); |
| 1182 | } |
| 1183 | }); |
| 1184 | }; |
| 1185 | if (batched_experts) { |
| 1186 | launch.template operator()<true>(); |
| 1187 | } else { |
| 1188 | launch.template operator()<false>(); |
| 1189 | } |
| 1190 | } |
| 1191 | |
| 1192 | #undef LAUNCH_MASKED_ACT_AND_MUL |
| 1193 | #undef LAUNCH_MASKED_ACTIVATION |
| 1194 | // Fused SITU activation and block-FP8 quantization for Humming w2. |
| 1195 | // `num_valid_tokens` is the int32 DeepEP token count (psum[-1]); rows = it * |
| 1196 | // `topk`, so padding is excluded on-device and skipped rows receive scale 1. |
| 1197 | void situ_and_mul_quant(torch::stable::Tensor& out, // [..., d] (fp8) |
| 1198 | torch::stable::Tensor& scale, // [..., 1 or d/128] |
| 1199 | torch::stable::Tensor& input, // [..., 2 * d] |
| 1200 | double beta, double linear_beta, int64_t group_size, |
| 1201 | std::optional<torch::stable::Tensor> num_valid_tokens, |
| 1202 | int64_t topk) { |
| 1203 | STD_TORCH_CHECK( |
| 1204 | out.scalar_type() == torch::headeronly::ScalarType::Float8_e4m3fn || |
| 1205 | out.scalar_type() == torch::headeronly::ScalarType::Float8_e4m3fnuz, |
| 1206 | "situ_and_mul_quant output must be FP8 e4m3"); |
| 1207 | STD_TORCH_CHECK( |
| 1208 | input.scalar_type() == torch::headeronly::ScalarType::Half || |
| 1209 | input.scalar_type() == torch::headeronly::ScalarType::BFloat16, |
| 1210 | "situ_and_mul_quant input must be FP16 or BF16"); |
| 1211 | STD_TORCH_CHECK(scale.scalar_type() == torch::headeronly::ScalarType::Float, |
| 1212 | "situ_and_mul_quant scale must be float32"); |
| 1213 | STD_TORCH_CHECK(input.is_cuda() && out.is_cuda() && scale.is_cuda() && |
| 1214 | out.get_device_index() == input.get_device_index() && |
| 1215 | scale.get_device_index() == input.get_device_index(), |
| 1216 | "situ_and_mul_quant: input, out and scale must be CUDA " |
| 1217 | "tensors on the same device"); |
| 1218 | int d = input.size(-1) / 2; |
| 1219 | int64_t num_tokens = input.numel() / input.size(-1); |
| 1220 | if (num_tokens == 0) { |
| 1221 | return; |
| 1222 | } |
| 1223 | STD_TORCH_CHECK(out.size(-1) == d && out.numel() == num_tokens * (int64_t)d, |
| 1224 | "situ_and_mul_quant: out shape must be [num_tokens, d]"); |
| 1225 | const int32_t* num_valid_tokens_ptr = nullptr; |
| 1226 | if (num_valid_tokens.has_value()) { |
| 1227 | STD_TORCH_CHECK( |
| 1228 | num_valid_tokens->is_cuda() && |
| 1229 | num_valid_tokens->get_device_index() == input.get_device_index(), |
| 1230 | "situ_and_mul_quant: num_valid_tokens must be a CUDA tensor on the " |
| 1231 | "same device as input"); |
| 1232 | STD_TORCH_CHECK( |
| 1233 | num_valid_tokens->scalar_type() == torch::headeronly::ScalarType::Int, |
| 1234 | "situ_and_mul_quant: num_valid_tokens must be int32 (psum count)"); |
| 1235 | num_valid_tokens_ptr = num_valid_tokens->const_data_ptr<int32_t>(); |
| 1236 | } |
| 1237 | const torch::stable::accelerator::DeviceGuard device_guard( |
| 1238 | input.get_device_index()); |
| 1239 | const cudaStream_t stream = get_current_cuda_stream(); |
| 1240 | static constexpr int BLOCKS_PER_SM = 8; // matches kernel __launch_bounds__ |
| 1241 | static constexpr int SM_COUNT = 132; // H200 (GH100, 132 SMs) |
| 1242 | static constexpr int GRID_DIM = SM_COUNT * BLOCKS_PER_SM; |
| 1243 | |
| 1244 | STD_TORCH_CHECK(group_size == 128, |
| 1245 | "situ_and_mul_quant: only group_size 128 (block-FP8) " |
| 1246 | "supported, got ", |
| 1247 | group_size); |
| 1248 | STD_TORCH_CHECK(d % group_size == 0, "situ_and_mul_quant: d (", d, |
| 1249 | ") must be divisible by group_size ", group_size); |
| 1250 | { |
| 1251 | const int num_groups = d / (int)group_size; |
| 1252 | STD_TORCH_CHECK(scale.size(-1) == num_groups && |
| 1253 | scale.numel() == num_tokens * (int64_t)num_groups, |
| 1254 | "situ_and_mul_quant: scale shape must be " |
| 1255 | "[num_tokens, d/group_size]"); |
| 1256 | dim3 grid(GRID_DIM); |
| 1257 | VLLM_STABLE_DISPATCH_FLOATING_TYPES( |
| 1258 | input.scalar_type(), "situ_and_mul_quant_group_kernel", [&] { |
| 1259 | VLLM_STABLE_DISPATCH_FP8_TYPES( |
| 1260 | out.scalar_type(), "situ_and_mul_quant_group_kernel_fp8", [&] { |
| 1261 | #ifndef USE_ROCM |
| 1262 | // The pipelined kernel's float2-per-lane geometry assumes a |
| 1263 | // 32-lane warp (GROUP_SIZE == 4 * WARP_SIZE); on HIP (64-lane) |
| 1264 | // fall back to the WARP_SIZE-generic scalar kernel. |
| 1265 | constexpr int THREADS = 256; |
| 1266 | constexpr int SITU_D = 3072; // Kimi-K3 fused w2 input dim |
| 1267 | if constexpr (sizeof(scalar_t) == 2) { |
| 1268 | if (d == SITU_D && (float)beta == vllm::SITU_BETA && |
| 1269 | (float)linear_beta == vllm::SITU_LINEAR_BETA) { |
| 1270 | constexpr int D = SITU_D; |
| 1271 | constexpr int GROUP_STAGES = 4; |
| 1272 | constexpr int NUM_WARPS = THREADS / 32; |
| 1273 | constexpr int STAGE_ELTS = 2 * 128; |
| 1274 | dim3 block(THREADS); |
| 1275 | size_t smem_bytes = (size_t)NUM_WARPS * GROUP_STAGES * |
| 1276 | STAGE_ELTS * sizeof(scalar_t); |
| 1277 | vllm::situ_and_mul_quant_group_pipelined_kernel< |
| 1278 | scalar_t, fp8_t, THREADS, GROUP_STAGES, 128, GRID_DIM, |
| 1279 | D><<<grid, block, smem_bytes, stream>>>( |
| 1280 | out.mutable_data_ptr<fp8_t>(), |
| 1281 | scale.mutable_data_ptr<float>(), |
| 1282 | input.const_data_ptr<scalar_t>(), num_tokens, |
| 1283 | num_valid_tokens_ptr, topk); |
| 1284 | return; |
| 1285 | } |
| 1286 | } |
| 1287 | #endif |
| 1288 | const int num_warps = std::min(num_groups, 1024 / WARP_SIZE); |
| 1289 | dim3 block(num_warps * WARP_SIZE); |
| 1290 | vllm::situ_and_mul_quant_group_scalar_kernel<scalar_t, fp8_t, |
| 1291 | 128> |
| 1292 | <<<grid, block, 0, stream>>>( |
| 1293 | out.mutable_data_ptr<fp8_t>(), |
| 1294 | scale.mutable_data_ptr<float>(), |
| 1295 | input.const_data_ptr<scalar_t>(), d, num_groups, |
| 1296 | (float)beta, (float)linear_beta, num_tokens, |
| 1297 | num_valid_tokens_ptr, topk); |
| 1298 | }); |
| 1299 | }); |
| 1300 | } |
| 1301 | } |
| 1302 | namespace vllm { |
| 1303 | |
| 1304 | // Element-wise activation kernel template. |
| 1305 | template <typename scalar_t, scalar_t (*ACT_FN)(const scalar_t&), bool use_vec, |
| 1306 | bool use_256b = false> |
| 1307 | __global__ void activation_kernel( |
| 1308 | scalar_t* __restrict__ out, // [..., d] |
| 1309 | const scalar_t* __restrict__ input, // [..., d] |
| 1310 | const int d) { |
| 1311 | const int64_t token_idx = blockIdx.x; |
| 1312 | const scalar_t* in_ptr = input + token_idx * d; |
| 1313 | scalar_t* out_ptr = out + token_idx * d; |
| 1314 | |
| 1315 | if constexpr (use_vec) { |
| 1316 | // Fast path: 128-bit/256-bit vectorized loop |
| 1317 | using vec_t = typename VecTraits<use_256b>::vec_t; |
| 1318 | constexpr int ARCH_MAX_VEC_SIZE = VecTraits<use_256b>::ARCH_MAX_VEC_SIZE; |
| 1319 | constexpr int VEC_SIZE = ARCH_MAX_VEC_SIZE / sizeof(scalar_t); |
| 1320 | const vec_t* in_vec = reinterpret_cast<const vec_t*>(in_ptr); |
| 1321 | vec_t* out_vec = reinterpret_cast<vec_t*>(out_ptr); |
| 1322 | const int num_vecs = d / VEC_SIZE; |
| 1323 | |
| 1324 | for (int i = threadIdx.x; i < num_vecs; i += blockDim.x) { |
| 1325 | vec_t v; |
| 1326 | if constexpr (use_256b) { |
| 1327 | ld256(v, &in_vec[i]); |
| 1328 | } else { |
| 1329 | v = VLLM_LDG(&in_vec[i]); |
| 1330 | } |
| 1331 | auto* vp = reinterpret_cast<scalar_t*>(&v); |
| 1332 | #pragma unroll |
| 1333 | for (int j = 0; j < VEC_SIZE; j++) { |
| 1334 | vp[j] = ACT_FN(vp[j]); |
| 1335 | } |
| 1336 | if constexpr (use_256b) { |
| 1337 | st256(v, &out_vec[i]); |
| 1338 | } else { |
| 1339 | out_vec[i] = v; |
| 1340 | } |
| 1341 | } |
| 1342 | } else { |
| 1343 | // Scalar fallback for unaligned data or small d |
| 1344 | for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) { |
| 1345 | const scalar_t x = VLLM_LDG(&in_ptr[idx]); |
| 1346 | out_ptr[idx] = ACT_FN(x); |
| 1347 | } |
| 1348 | } |
| 1349 | } |
| 1350 | |
| 1351 | } // namespace vllm |
| 1352 | |
| 1353 | // Launch element-wise activation kernel. |
| 1354 | #define LAUNCH_ACTIVATION_KERNEL(KERNEL) \ |
| 1355 | auto dtype = input.scalar_type(); \ |
| 1356 | int d = input.size(-1); \ |
| 1357 | int64_t num_tokens = input.numel() / input.size(-1); \ |
| 1358 | if (num_tokens == 0) { \ |
| 1359 | return; \ |
| 1360 | } \ |
| 1361 | dim3 grid(num_tokens); \ |
| 1362 | int cc_major = get_device_prop()->major; \ |
| 1363 | int support_vec = \ |
| 1364 | (CUDA_VERSION >= 12090 && cc_major >= 10 && num_tokens > 128) \ |
| 1365 | ? vllm::VecTraits<true>::ARCH_MAX_VEC_SIZE \ |
| 1366 | : vllm::VecTraits<false>::ARCH_MAX_VEC_SIZE; \ |
| 1367 | int vec_size = support_vec / input.element_size(); \ |
| 1368 | const bool use_vec = (d % vec_size == 0); \ |
| 1369 | const torch::stable::accelerator::DeviceGuard device_guard( \ |
| 1370 | input.get_device_index()); \ |
| 1371 | const cudaStream_t stream = get_current_cuda_stream(); \ |
| 1372 | if (use_vec) { \ |
| 1373 | dim3 block(std::min(d / vec_size, 1024)); \ |
| 1374 | if (CUDA_VERSION >= 12090 && cc_major >= 10 && num_tokens > 128) { \ |
| 1375 | VLLM_STABLE_DISPATCH_FLOATING_TYPES(dtype, "activation_kernel", [&] { \ |
| 1376 | vllm::activation_kernel<scalar_t, KERNEL<scalar_t>, true, true> \ |
| 1377 | <<<grid, block, 0, stream>>>(out.mutable_data_ptr<scalar_t>(), \ |
| 1378 | input.const_data_ptr<scalar_t>(), d); \ |
| 1379 | }); \ |
| 1380 | } else { \ |
| 1381 | VLLM_STABLE_DISPATCH_FLOATING_TYPES(dtype, "activation_kernel", [&] { \ |
| 1382 | vllm::activation_kernel<scalar_t, KERNEL<scalar_t>, true, false> \ |
| 1383 | <<<grid, block, 0, stream>>>(out.mutable_data_ptr<scalar_t>(), \ |
| 1384 | input.const_data_ptr<scalar_t>(), d); \ |
| 1385 | }); \ |
| 1386 | } \ |
| 1387 | } else { \ |
| 1388 | dim3 block(std::min(d, 1024)); \ |
| 1389 | VLLM_STABLE_DISPATCH_FLOATING_TYPES(dtype, "activation_kernel", [&] { \ |
| 1390 | vllm::activation_kernel<scalar_t, KERNEL<scalar_t>, false> \ |
| 1391 | <<<grid, block, 0, stream>>>(out.mutable_data_ptr<scalar_t>(), \ |
| 1392 | input.const_data_ptr<scalar_t>(), d); \ |
| 1393 | }); \ |
| 1394 | } |
| 1395 | |
| 1396 | namespace vllm { |
| 1397 | |
| 1398 | template <typename T> |
| 1399 | __device__ __forceinline__ T gelu_new_kernel(const T& x) { |
| 1400 | const float x3 = (float)(x * x * x); |
| 1401 | const T t = (T)tanhf((T)(0.79788456f * (float)(x + (T)(0.044715f * x3)))); |
| 1402 | return ((T)0.5) * x * (((T)1.0) + t); |
| 1403 | } |
| 1404 | |
| 1405 | template <typename T> |
| 1406 | __device__ __forceinline__ T gelu_fast_kernel(const T& x) { |
| 1407 | const float f = (float)x; |
| 1408 | const T t = |
| 1409 | (T)tanhf(((T)(f * 0.79788456f)) * (((T)1.0) + (T)(0.044715f * f) * x)); |
| 1410 | return ((T)0.5) * x * (((T)1.0) + t); |
| 1411 | } |
| 1412 | |
| 1413 | template <typename T> |
| 1414 | __device__ __forceinline__ T gelu_quick_kernel(const T& x) { |
| 1415 | // x * sigmoid(1.702 * x) |
| 1416 | return (T)(((float)x) / (1.0f + expf(-1.702f * (float)x))); |
| 1417 | } |
| 1418 | |
| 1419 | template <typename T> |
| 1420 | __device__ __forceinline__ T relu_squared_kernel(const T& x) { |
| 1421 | // relu(x)^2 — introduced in https://arxiv.org/abs/2109.08668v2 |
| 1422 | const float f = (float)x; |
| 1423 | const float val = f > 0.0f ? f : 0.0f; |
| 1424 | return (T)(val * val); |
| 1425 | } |
| 1426 | |
| 1427 | } // namespace vllm |
| 1428 | |
| 1429 | void gelu_new(torch::stable::Tensor& out, // [..., d] |
| 1430 | torch::stable::Tensor& input) // [..., d] |
| 1431 | { |
| 1432 | LAUNCH_ACTIVATION_KERNEL(vllm::gelu_new_kernel); |
| 1433 | } |
| 1434 | |
| 1435 | void gelu_fast(torch::stable::Tensor& out, // [..., d] |
| 1436 | torch::stable::Tensor& input) // [..., d] |
| 1437 | { |
| 1438 | LAUNCH_ACTIVATION_KERNEL(vllm::gelu_fast_kernel); |
| 1439 | } |
| 1440 | |
| 1441 | void gelu_quick(torch::stable::Tensor& out, // [..., d] |
| 1442 | torch::stable::Tensor& input) // [..., d] |
| 1443 | { |
| 1444 | LAUNCH_ACTIVATION_KERNEL(vllm::gelu_quick_kernel); |
| 1445 | } |
| 1446 | |
| 1447 | void relu_squared(torch::stable::Tensor& out, // [..., d] |
| 1448 | torch::stable::Tensor& input) // [..., d] |
| 1449 | { |
| 1450 | LAUNCH_ACTIVATION_KERNEL(vllm::relu_squared_kernel); |
| 1451 | } |