Compare commits

...
Author SHA1 Message Date
Alexander Matveev ba25edb264 add file
Signed-off-by: Alexander Matveev <amatveev@redhat.com>
2025-11-26 13:56:58 +00:00
Barbara SuslovaandAlexander Matveev 9f3812d001 aligning
Signed-off-by: Barbara Suslova <barbara.suslova@axel-t.com>
2025-11-25 15:31:48 +00:00
Barbara SuslovaandAlexander Matveev d8dbb8c0c4 change the logic of passing variables
Signed-off-by: Barbara Suslova <barbara.suslova@axel-t.com>
2025-11-25 15:31:46 +00:00
Barbara SuslovaandAlexander Matveev 4d9f1ad714 lint
Signed-off-by: Barbara Suslova <barbara.suslova@axel-t.com>
2025-11-25 15:28:46 +00:00
Barbara SuslovaandAlexander Matveev c13434bbb8 add fusion of shared expert and fused_moe_gate
Signed-off-by: Barbara Suslova <barbara.suslova@axel-t.com>
2025-11-25 15:28:17 +00:00
22 changed files with 1061 additions and 44 deletions
+2 -1
View File
@@ -909,7 +909,8 @@ set(VLLM_MOE_EXT_SRC
"csrc/moe/torch_bindings.cpp" "csrc/moe/torch_bindings.cpp"
"csrc/moe/moe_align_sum_kernels.cu" "csrc/moe/moe_align_sum_kernels.cu"
"csrc/moe/moe_lora_align_sum_kernels.cu" "csrc/moe/moe_lora_align_sum_kernels.cu"
"csrc/moe/topk_softmax_kernels.cu") "csrc/moe/topk_softmax_kernels.cu"
"csrc/moe/moe_fused_gate.cu")
if(VLLM_GPU_LANG STREQUAL "CUDA") if(VLLM_GPU_LANG STREQUAL "CUDA")
list(APPEND VLLM_MOE_EXT_SRC list(APPEND VLLM_MOE_EXT_SRC
@@ -0,0 +1,75 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import torch
from vllm._custom_ops import moe_fused_gate
from vllm.model_executor.layers.fused_moe.fused_moe import (
grouped_topk as vllm_compiled_grouped_topk,
)
from vllm.triton_utils import triton
def biased_grouped_topk_org(scores, bias, num_expert_group, topk_group, topk):
return vllm_compiled_grouped_topk(
hidden_states=scores,
gating_output=scores,
topk=topk,
renormalize=True,
num_expert_group=num_expert_group,
topk_group=topk_group,
scoring_func="sigmoid",
e_score_correction_bias=bias,
)
def biased_grouped_topk_org_kernel(scores, bias, num_expert_group, topk_group, topk):
return moe_fused_gate(scores, bias, num_expert_group, topk_group, topk)
seq_length_range = [5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000]
configs = [(sq,) for sq in seq_length_range]
@triton.testing.perf_report(
triton.testing.Benchmark(
x_names=["seq_length"],
x_vals=[list(_) for _ in configs],
line_arg="provider",
line_vals=["original", "kernel"],
line_names=["Original", "SGL Kernel"],
styles=[("blue", "-"), ("red", "-")],
ylabel="us",
plot_name="moe-fused-gate-performance",
args={},
)
)
def benchmark(seq_length, provider):
dtype = torch.bfloat16
device = torch.device("cuda")
num_experts, num_expert_group, topk_group, topk = 256, 8, 4, 8
scores = torch.randn((seq_length, num_experts), device=device, dtype=dtype)
bias = torch.rand(num_experts, device=device, dtype=dtype)
quantiles = [0.5, 0.2, 0.8]
if provider == "original":
ms, min_ms, max_ms = triton.testing.do_bench(
lambda: biased_grouped_topk_org(
scores.clone(), bias.clone(), num_expert_group, topk_group, topk
),
quantiles=quantiles,
)
elif provider == "kernel":
ms, min_ms, max_ms = triton.testing.do_bench(
lambda: biased_grouped_topk_org_kernel(
scores.clone(), bias.clone(), num_expert_group, topk_group, topk
),
quantiles=quantiles,
)
return 1000 * ms, 1000 * max_ms, 1000 * min_ms
if __name__ == "__main__":
benchmark.run(print_data=True)
+484
View File
@@ -0,0 +1,484 @@
#include <ATen/cuda/CUDAContext.h>
#include <cuda_runtime.h>
#include <cutlass/array.h>
#include <cutlass/cutlass.h>
#include <cutlass/numeric_types.h>
#include <stdio.h>
#include <torch/all.h>
#include <cfloat>
#include <type_traits>
template <typename T, int N>
using AlignedArray = cutlass::AlignedArray<T, N>;
using bfloat16_t = cutlass::bfloat16_t;
using float16_t = cutlass::half_t;
using float32_t = float;
// QQ NOTE: to handle the case for at::Half, error: more than one operator ">"
// matches these operands: built-in operator "arithmetic > arithmetic" function
// "operator>(const __half &, const __half &)"
template <typename T>
__device__ inline bool cmp_gt(const T& a, const T& b) {
if constexpr (std::is_same<T, at::Half>::value) {
// at::Half (or float16_t in our native case) causes ambiguity, so we cast
// to float.
return static_cast<float>(a) > static_cast<float>(b);
} else {
// For types like float, at::BFloat16, or cutlass::half_t /
// cutlass::bfloat16_t, assume operator> works as expected.
return a > b;
}
}
template <typename T>
__device__ inline bool cmp_eq(const T& a, const T& b) {
if constexpr (std::is_same<T, at::Half>::value) {
return static_cast<float>(a) == static_cast<float>(b);
} else {
return a == b;
}
}
// Fixed constants common to both dynamic and static template versions:
static constexpr int WARP_SIZE = 32;
static constexpr int WARPS_PER_CTA = 6;
static constexpr int MAX_VPT =
32; // maximum VPT we support, > params.VPT = num_expert / num_expert_group
// Create an alias for Array using AlignedArray
template <typename T, int N>
using Array = AlignedArray<T, N>;
// QQ: NOTE expression must have a constant value, this has to be > params.VPT
template <typename T>
using AccessType = AlignedArray<T, MAX_VPT>;
template <typename T, typename Params>
__device__ void moe_fused_gate_impl(void* input, void* bias, float* output_ptr,
int32_t* indices_ptr, int64_t num_rows,
int64_t topk_group, int64_t topk,
int64_t num_fused_shared_experts,
double routed_scaling_factor,
bool apply_routed_scaling_factor_on_output,
Params params) {
int tidx = threadIdx.x;
int64_t thread_row = blockIdx.x * params.ROWS_PER_CTA +
threadIdx.y * params.ROWS_PER_WARP +
tidx / params.THREADS_PER_ROW;
if (thread_row >= num_rows) {
return;
}
// Calculate topk_excluding_share_expert_fusion from topk
int64_t topk_excluding_share_expert_fusion = topk - num_fused_shared_experts;
// Cast pointers to type T:
auto* input_ptr = reinterpret_cast<T*>(input);
auto* bias_ptr = reinterpret_cast<T*>(bias);
auto* thread_row_ptr = input_ptr + thread_row * params.NUM_EXPERTS;
int thread_group_idx = tidx % params.THREADS_PER_ROW;
int first_elt_read_by_thread = thread_group_idx * params.VPT;
// Create local arrays for the row chunk and bias chunk and then reinterpret
// the address of row_chunk as a pointer to AccessType.
T* thread_read_ptr = thread_row_ptr + first_elt_read_by_thread;
Array<T, MAX_VPT> row_chunk;
AccessType<T> const* vec_thread_read_ptr =
reinterpret_cast<AccessType<T> const*>(thread_read_ptr);
T* bias_thread_read_ptr = bias_ptr + first_elt_read_by_thread;
Array<T, MAX_VPT> bias_chunk;
AccessType<T> const* vec_bias_thread_read_ptr =
reinterpret_cast<AccessType<T> const*>(bias_thread_read_ptr);
// QQ NOTE: doing the follow will be slower than loop assign and more
// importantly have misaligned address issue when params.VPT < 8 and mismatch
// with MAX_VPT AccessType<T>* row_chunk_vec_ptr =
// reinterpret_cast<AccessType<T>*>(&row_chunk); row_chunk_vec_ptr[0] =
// vec_thread_read_ptr[0];
#pragma unroll
for (int ii = 0; ii < params.VPT; ++ii) {
row_chunk[ii] = vec_thread_read_ptr[0][ii];
bias_chunk[ii] = vec_bias_thread_read_ptr[0][ii];
}
__syncthreads();
////////////////////// Sigmoid //////////////////////
#pragma unroll
for (int ii = 0; ii < params.VPT; ++ii) {
row_chunk[ii] = static_cast<T>(1.0f / (1.0f + expf(-float(row_chunk[ii]))));
}
__syncthreads();
////////////////////// Add Bias //////////////////////
#pragma unroll
for (int ii = 0; ii < params.VPT; ++ii) {
bias_chunk[ii] = row_chunk[ii] + bias_chunk[ii];
}
////////////////////// Exclude Groups //////////////////////
#pragma unroll
for (int k_idx = 0; k_idx < params.THREADS_PER_ROW - topk_group;
++k_idx) { // QQ NOTE Here params.THREADS_PER_ROW = num_expert_group
int expert = first_elt_read_by_thread;
// local argmax
T max_val = static_cast<T>(-FLT_MAX);
T max_val_second = static_cast<T>(-FLT_MAX);
#pragma unroll
for (int ii = 0; ii < params.VPT; ++ii) {
T val = bias_chunk[ii];
if (cmp_gt(val, max_val)) {
max_val_second = max_val;
max_val = val;
} else if (cmp_gt(val, max_val_second)) {
max_val_second = val;
}
}
// QQ NOTE: currently fixed to pick top2 sigmoid weight value in each expert
// group and sum them as the group weight to select expert groups
T max_sum = max_val + max_val_second;
// argmin reduce
#pragma unroll
for (int mask = params.THREADS_PER_ROW / 2; mask > 0; mask /= 2) {
T other_max_sum = static_cast<T>(
__shfl_xor_sync(0xFFFFFFFF, static_cast<float>(max_sum), mask,
params.THREADS_PER_ROW));
int other_expert =
__shfl_xor_sync(0xFFFFFFFF, expert, mask, params.THREADS_PER_ROW);
// higher indices win
if (cmp_gt(max_sum, other_max_sum) ||
(cmp_eq(other_max_sum, max_sum) && other_expert > expert)) {
max_sum = other_max_sum;
expert = other_expert;
}
}
// clear the max value in the thread
if (k_idx < params.THREADS_PER_ROW - topk_group) {
int const thread_to_clear_in_group = expert / params.VPT;
if (thread_group_idx == thread_to_clear_in_group) {
#pragma unroll
for (int ii = 0; ii < params.VPT; ++ii) {
bias_chunk[ii] = static_cast<T>(FLT_MAX);
}
}
}
}
__syncthreads();
////////////////////// Topk //////////////////////
float output_sum = 0.0f;
for (int k_idx = 0; k_idx < topk_excluding_share_expert_fusion; ++k_idx) {
// local argmax
T max_val = bias_chunk[0];
int expert = first_elt_read_by_thread;
if (!cmp_eq(max_val, static_cast<T>(FLT_MAX))) {
#pragma unroll
for (int ii = 1; ii < params.VPT; ++ii) {
T val = bias_chunk[ii];
if (cmp_gt(val, max_val)) {
max_val = val;
expert = first_elt_read_by_thread + ii;
}
}
} else {
max_val = static_cast<T>(-FLT_MAX);
}
// argmax reduce
#pragma unroll
for (int mask = params.THREADS_PER_ROW / 2; mask > 0; mask /= 2) {
T other_max = static_cast<T>(
__shfl_xor_sync(0xFFFFFFFF, static_cast<float>(max_val), mask,
params.THREADS_PER_ROW));
int other_expert =
__shfl_xor_sync(0xFFFFFFFF, expert, mask, params.THREADS_PER_ROW);
// lower indices to win
if (cmp_gt(other_max, max_val) ||
(cmp_eq(other_max, max_val) && other_expert < expert)) {
max_val = other_max;
expert = other_expert;
}
}
int thread_to_clear_in_group = expert / params.VPT;
int64_t idx = topk * thread_row + k_idx;
if (thread_group_idx == thread_to_clear_in_group) {
int expert_to_clear_in_thread = expert % params.VPT;
// clear the max value in the thread
bias_chunk[expert_to_clear_in_thread] = static_cast<T>(-FLT_MAX);
// store output
output_ptr[idx] =
static_cast<float>(row_chunk[expert_to_clear_in_thread]);
indices_ptr[idx] = static_cast<int32_t>(expert);
}
// accumulate sum for all elements
if (thread_group_idx == 0) {
output_sum += output_ptr[idx];
}
__syncthreads();
}
if (thread_group_idx == 0 && num_fused_shared_experts > 0) {
int64_t last_idx = topk * thread_row + topk_excluding_share_expert_fusion;
int64_t expert_offset = 0;
indices_ptr[last_idx] =
static_cast<int32_t>(params.NUM_EXPERTS + expert_offset);
// Set the weight to the sum of all weights divided by routed_scaling_factor
output_ptr[last_idx] = output_sum / routed_scaling_factor;
if (num_fused_shared_experts > 1) {
for (int i = 1; i < num_fused_shared_experts; ++i) {
++last_idx;
++expert_offset;
indices_ptr[last_idx] =
static_cast<int32_t>(params.NUM_EXPERTS + expert_offset);
// Set the weight to the sum of all weights divided by
// routed_scaling_factor
output_ptr[last_idx] = output_sum / routed_scaling_factor;
}
}
}
__syncthreads();
////////////////////// Rescale Output //////////////////////
if (thread_group_idx == 0) {
#pragma unroll
for (int ii = 0; ii < topk; ++ii) {
int64_t const idx = topk * thread_row + ii;
output_ptr[idx] = output_ptr[idx] / output_sum;
if (apply_routed_scaling_factor_on_output) {
output_ptr[idx] *= routed_scaling_factor;
}
}
}
}
//------------------------------------------------------------------------------
// Templated Kernel Version (using compile-time constants)
//------------------------------------------------------------------------------
template <int VPT_, int NUM_EXPERTS_, int THREADS_PER_ROW_, int ROWS_PER_WARP_,
int ROWS_PER_CTA_, int WARPS_PER_CTA_>
struct KernelParams {
static constexpr int VPT = VPT_;
static constexpr int NUM_EXPERTS = NUM_EXPERTS_;
static constexpr int THREADS_PER_ROW = THREADS_PER_ROW_;
static constexpr int ROWS_PER_WARP = ROWS_PER_WARP_;
static constexpr int ROWS_PER_CTA = ROWS_PER_CTA_;
static constexpr int WARPS_PER_CTA = WARPS_PER_CTA_;
};
template <typename T, int VPT, int NUM_EXPERTS, int THREADS_PER_ROW,
int ROWS_PER_WARP, int ROWS_PER_CTA, int WARPS_PER_CTA>
__global__ void moe_fused_gate_kernel(
void* input, void* bias, float* output_ptr, int32_t* indices_ptr,
int64_t num_rows, int64_t topk_group, int64_t topk,
int64_t num_fused_shared_experts, double routed_scaling_factor,
bool apply_routed_scaling_factor_on_output) {
KernelParams<VPT, NUM_EXPERTS, THREADS_PER_ROW, ROWS_PER_WARP, ROWS_PER_CTA,
WARPS_PER_CTA>
params;
moe_fused_gate_impl<T>(input, bias, output_ptr, indices_ptr, num_rows,
topk_group, topk, num_fused_shared_experts,
routed_scaling_factor,
apply_routed_scaling_factor_on_output, params);
}
// Macro to compute compile-time constants and launch the kernel.
#define LAUNCH_MOE_GATE_CONFIG(T, EXPERTS, EXPERT_GROUP) \
do { \
constexpr int VPT = (EXPERTS) / (EXPERT_GROUP); \
/* If EXPERT_GROUP > WARP_SIZE, fall back to 1 row per warp */ \
constexpr int ROWS_PER_WARP = \
((EXPERT_GROUP) <= WARP_SIZE) ? (WARP_SIZE / (EXPERT_GROUP)) : 1; \
constexpr int ROWS_PER_CTA = WARPS_PER_CTA * ROWS_PER_WARP; \
moe_fused_gate_kernel<T, VPT, (EXPERTS), (EXPERT_GROUP), ROWS_PER_WARP, \
ROWS_PER_CTA, WARPS_PER_CTA> \
<<<num_blocks, block_dim, 0, stream>>>( \
input.data_ptr(), bias.data_ptr(), output.data_ptr<float>(), \
indices.data_ptr<int32_t>(), num_rows, topk_group, topk, \
num_fused_shared_experts, routed_scaling_factor, \
apply_routed_scaling_factor_on_output); \
dispatched = true; \
} while (0)
//------------------------------------------------------------------------------
// Dynamic Kernel Version (parameters computed at runtime)
//------------------------------------------------------------------------------
struct KernelParamsDynamic {
int VPT;
int NUM_EXPERTS;
int THREADS_PER_ROW;
int ROWS_PER_WARP;
int ROWS_PER_CTA;
int WARPS_PER_CTA;
};
template <typename T>
__global__ void moe_fused_gate_kernel_dynamic(
void* input, void* bias, float* output_ptr, int32_t* indices_ptr,
int64_t num_rows, int64_t num_experts, int64_t num_expert_group,
int64_t topk_group, int64_t topk, int64_t num_fused_shared_experts,
double routed_scaling_factor, bool apply_routed_scaling_factor_on_output) {
KernelParamsDynamic params;
params.NUM_EXPERTS = num_experts; // e.g, for deepseek v3, this is 256
params.VPT = num_experts /
num_expert_group; // e.g., for deepseek v3, this is 256 / 8 = 32
params.THREADS_PER_ROW =
num_expert_group; // fixed as num_expert_group, e.g., for deepseek v3,
// this is 8
params.WARPS_PER_CTA = WARPS_PER_CTA; // fixed as 6
params.ROWS_PER_WARP = std::max<int64_t>(
1, WARP_SIZE / num_expert_group); // WARP_SIZE is fixed as 32
params.ROWS_PER_CTA = params.WARPS_PER_CTA * params.ROWS_PER_WARP;
moe_fused_gate_impl<T>(input, bias, output_ptr, indices_ptr, num_rows,
topk_group, topk, num_fused_shared_experts,
routed_scaling_factor,
apply_routed_scaling_factor_on_output, params);
}
//------------------------------------------------------------------------------
// Host Launcher Function
//------------------------------------------------------------------------------
std::vector<at::Tensor> moe_fused_gate(
at::Tensor& input, at::Tensor& bias, int64_t num_expert_group,
int64_t topk_group, int64_t topk, int64_t num_fused_shared_experts,
double routed_scaling_factor, bool apply_routed_scaling_factor_on_output) {
TORCH_CHECK(input.dtype() == bias.dtype(),
"input and bias should have the same dtype");
int64_t num_rows = input.size(0);
int32_t num_experts = input.size(1);
auto options =
torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA);
auto output = torch::empty({num_rows, topk}, options);
auto indices = torch::empty({num_rows, topk}, options.dtype(torch::kInt32));
// Compute grid dimensions based on runtime value for num_expert_group.
int64_t rows_per_warp = std::max<int64_t>(1, WARP_SIZE / num_expert_group);
int64_t num_warps = (num_rows + rows_per_warp - 1) / rows_per_warp;
int64_t num_blocks = (num_warps + WARPS_PER_CTA - 1) / WARPS_PER_CTA;
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
dim3 block_dim(WARP_SIZE, WARPS_PER_CTA);
// Check 1: Ensure that num_experts is a power of 2.
TORCH_CHECK((num_experts & (num_experts - 1)) == 0,
"num_experts must be a power of 2, but got ", num_experts);
// Check 2: Ensure that num_experts is divisible by num_expert_group. (this
// also means num_expert_group is power of 2)
TORCH_CHECK(num_experts % num_expert_group == 0,
"num_experts must be divisible by num_expert_group, but got ",
num_experts, " / ", num_expert_group);
int computed_vpt = num_experts / num_expert_group;
// Check 3: Ensure that num_experts/num_expert_group does not exceed
// MAX_VPT=32. Maximum VPT indicate max value per threads we can process.
TORCH_CHECK(computed_vpt <= MAX_VPT,
"Per group experts: num_experts / num_expert_group = (",
computed_vpt, ") exceeds the maximum supported (", MAX_VPT, ")");
// Dispatch to templated kernel for known compile-time configurations.
// We currently only support for:
// Case 1: 256 experts, with 8 or 16 groups.
// Case 2: 128 experts, with 4 or 8 groups.
// Case 3: other cases, require 8 <= num_experts / num_expert_group <= 32
bool dispatched = false;
switch (num_experts) {
case 256:
if (num_expert_group == 8) {
// This is deepseek v3 case. Here VPT = 256/8 = 32, ROWS_PER_WARP = 32/8
// = 4, ROWS_PER_CTA = 6 * 4 = 24.
if (input.scalar_type() == at::kBFloat16) {
LAUNCH_MOE_GATE_CONFIG(bfloat16_t, 256, 8);
} else if (input.scalar_type() == at::kHalf) {
LAUNCH_MOE_GATE_CONFIG(float16_t, 256, 8);
} else if (input.scalar_type() == at::kFloat) {
LAUNCH_MOE_GATE_CONFIG(float32_t, 256, 8);
}
} else if (num_expert_group == 16) {
// Here VPT = 256/16 = 16, ROWS_PER_WARP = 32/16 = 2, ROWS_PER_CTA = 6
// * 2 = 12.
if (input.scalar_type() == at::kBFloat16) {
LAUNCH_MOE_GATE_CONFIG(bfloat16_t, 256, 16);
} else if (input.scalar_type() == at::kHalf) {
LAUNCH_MOE_GATE_CONFIG(float16_t, 256, 16);
} else if (input.scalar_type() == at::kFloat) {
LAUNCH_MOE_GATE_CONFIG(float32_t, 256, 16);
}
}
break;
case 128:
if (num_expert_group == 4) {
// VPT = 128/4 = 32, ROWS_PER_WARP = 32/16 = 2, ROWS_PER_CTA = 6 * 2
// = 12.
if (input.scalar_type() == at::kBFloat16) {
LAUNCH_MOE_GATE_CONFIG(bfloat16_t, 128, 4);
} else if (input.scalar_type() == at::kHalf) {
LAUNCH_MOE_GATE_CONFIG(float16_t, 128, 4);
} else if (input.scalar_type() == at::kFloat) {
LAUNCH_MOE_GATE_CONFIG(float32_t, 128, 4);
}
} else if (num_expert_group == 8) {
// VPT = 128/8 = 16, ROWS_PER_WARP = 32/8 = 4, ROWS_PER_CTA = 6 * 4
// = 24.
if (input.scalar_type() == at::kBFloat16) {
LAUNCH_MOE_GATE_CONFIG(bfloat16_t, 128, 8);
} else if (input.scalar_type() == at::kHalf) {
LAUNCH_MOE_GATE_CONFIG(float16_t, 128, 8);
} else if (input.scalar_type() == at::kFloat) {
LAUNCH_MOE_GATE_CONFIG(float32_t, 128, 8);
}
}
break;
default:
break;
}
if (!dispatched) {
// Fallback to the dynamic kernel if none of the supported combinations
// match. currently only support num_experts / num_expert_group <= 32 for
// dynamic kernels
if (input.scalar_type() == at::kBFloat16) {
moe_fused_gate_kernel_dynamic<bfloat16_t>
<<<num_blocks, block_dim, 0, stream>>>(
input.data_ptr(), bias.data_ptr(), output.data_ptr<float>(),
indices.data_ptr<int32_t>(), num_rows, num_experts,
num_expert_group, topk_group, topk, num_fused_shared_experts,
routed_scaling_factor, apply_routed_scaling_factor_on_output);
} else if (input.scalar_type() == at::kHalf) {
moe_fused_gate_kernel_dynamic<float16_t>
<<<num_blocks, block_dim, 0, stream>>>(
input.data_ptr(), bias.data_ptr(), output.data_ptr<float>(),
indices.data_ptr<int32_t>(), num_rows, num_experts,
num_expert_group, topk_group, topk, num_fused_shared_experts,
routed_scaling_factor, apply_routed_scaling_factor_on_output);
} else if (input.scalar_type() == at::kFloat) {
moe_fused_gate_kernel_dynamic<float32_t>
<<<num_blocks, block_dim, 0, stream>>>(
input.data_ptr(), bias.data_ptr(), output.data_ptr<float>(),
indices.data_ptr<int32_t>(), num_rows, num_experts,
num_expert_group, topk_group, topk, num_fused_shared_experts,
routed_scaling_factor, apply_routed_scaling_factor_on_output);
} else {
TORCH_CHECK(false, "Unsupported data type for moe_fused_gate");
}
}
return {output, indices};
}
+6
View File
@@ -27,6 +27,12 @@ void moe_lora_align_block_size(
torch::Tensor sorted_token_ids, torch::Tensor expert_ids, torch::Tensor sorted_token_ids, torch::Tensor expert_ids,
torch::Tensor num_tokens_post_pad, torch::Tensor adapter_enabled, torch::Tensor num_tokens_post_pad, torch::Tensor adapter_enabled,
torch::Tensor lora_ids); torch::Tensor lora_ids);
std::vector<at::Tensor> moe_fused_gate(
torch::Tensor& input, torch::Tensor& bias, int64_t num_expert_group,
int64_t topk_group, int64_t topk, int64_t num_fused_shared_experts,
double routed_scaling_factor, bool apply_routed_scaling_factor_on_output);
#ifndef USE_ROCM #ifndef USE_ROCM
torch::Tensor moe_wna16_gemm(torch::Tensor input, torch::Tensor output, torch::Tensor moe_wna16_gemm(torch::Tensor input, torch::Tensor output,
torch::Tensor b_qweight, torch::Tensor b_scales, torch::Tensor b_qweight, torch::Tensor b_scales,
+7
View File
@@ -48,6 +48,13 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, m) {
" Tensor !adapter_enabled," " Tensor !adapter_enabled,"
" Tensor !lora_ids) -> () "); " Tensor !lora_ids) -> () ");
m.impl("moe_lora_align_block_size", torch::kCUDA, &moe_lora_align_block_size); m.impl("moe_lora_align_block_size", torch::kCUDA, &moe_lora_align_block_size);
m.def(
"moe_fused_gate(Tensor input, Tensor bias, int num_expert_group, int "
"topk_group, int topk, int "
"num_fused_shared_experts, float routed_scaling_factor, "
"bool apply_routed_scaling_factor_on_output) -> "
"(Tensor[])");
m.impl("moe_fused_gate", torch::kCUDA, &moe_fused_gate);
#ifndef USE_ROCM #ifndef USE_ROCM
m.def( m.def(
+4 -1
View File
@@ -81,7 +81,7 @@ MNK_FACTORS_DG = [
] ]
BLOCK_SIZE = [[128, 128]] BLOCK_SIZE = [[128, 128]]
E = [2, 8, 16] # [128, 256] E = [2, 8, 16, 258] # [128, 256]
TOP_KS = [1, 2, 6] TOP_KS = [1, 2, 6]
SEEDS = [0] SEEDS = [0]
@@ -144,6 +144,9 @@ def test_w8a8_block_fp8_fused_moe(
a = torch.randn((M, K), dtype=dtype) / 10 a = torch.randn((M, K), dtype=dtype) / 10
score = torch.randn((M, E), dtype=dtype) score = torch.randn((M, E), dtype=dtype)
if E == 258:
monkeypatch.setenv("VLLM_USE_CUDA_FUSION_SHARED_EXPERTS", "1")
w1, w2, quant_config = make_test_quant_config( w1, w2, quant_config = make_test_quant_config(
E, E,
N, N,
+117
View File
@@ -0,0 +1,117 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import pytest
import torch
from vllm import _custom_ops as ops
from vllm.model_executor.layers.fused_moe.fused_moe import grouped_topk
@pytest.mark.parametrize(
"seq_length",
list(range(1, 10))
+ [16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536],
)
@pytest.mark.parametrize(
"dtype",
[torch.float32], # torch.float16, torch.bfloat16 - aren't working correctly yet
)
@pytest.mark.parametrize(
"params",
[
# (128, 4, 2, 4),
(256, 8, 4, 8), # deepseek v3
# (512, 16, 8, 16),
],
)
@pytest.mark.parametrize(
"num_fused_shared_experts",
[
0,
1,
],
)
def test_moe_fused_gate_combined(
seq_length, dtype, params, num_fused_shared_experts, monkeypatch
):
num_experts, num_expert_group, topk_group, topk = params
topk += 1 if num_fused_shared_experts > 0 else 0
torch.manual_seed(seq_length)
tensor = torch.rand((seq_length, num_experts)).to(dtype).cuda()
scores = tensor.clone()
bias = torch.rand(num_experts).to(dtype).cuda()
routed_scaling_factor = 2.5
output, indices = ops.moe_fused_gate(
tensor,
bias,
num_expert_group=num_expert_group,
topk_group=topk_group,
topk=topk,
num_fused_shared_experts=num_fused_shared_experts,
routed_scaling_factor=routed_scaling_factor,
apply_routed_scaling_factor_on_output=True,
)
monkeypatch.setenv("VLLM_USE_FUSED_MOE_GROUPED_TOPK", "0")
ref_vllm_output, ref_vllm_indices = grouped_topk(
hidden_states=scores,
gating_output=scores,
topk=topk,
renormalize=True,
num_expert_group=num_expert_group,
topk_group=topk_group,
scoring_func="sigmoid",
e_score_correction_bias=bias,
num_fused_shared_experts=num_fused_shared_experts,
routed_scaling_factor=routed_scaling_factor,
)
if num_fused_shared_experts > 0:
original_indices = indices.clone()
original_ref_indices = ref_vllm_indices.clone()
indices = indices[:, :-1]
ref_vllm_indices = ref_vllm_indices[:, :-1]
valid_min = num_experts
valid_max = num_experts + num_fused_shared_experts
shared_indices = original_indices[:, -1]
shared_ref_indices = original_ref_indices[:, -1]
if shared_indices is not None:
assert torch.all(
(shared_indices >= valid_min) & (shared_indices < valid_max)
), (
"Shared expert indices out of range: ",
f"found values outside [{valid_min}, {valid_max})",
)
if shared_ref_indices is not None:
assert torch.all(
(shared_ref_indices >= valid_min) & (shared_ref_indices < valid_max)
), (
"Shared expert reference indices out of range: ",
f"found values outside [{valid_min}, {valid_max})",
)
vllm_idx_check = torch.allclose(
ref_vllm_indices.sort()[0].to(torch.int32),
indices.sort()[0].to(torch.int32),
rtol=1e-04,
atol=1e-05,
)
vllm_output_check = torch.allclose(
ref_vllm_output.sort()[0].to(torch.float32),
output.sort()[0].to(torch.float32),
rtol=1e-04,
atol=1e-03,
)
assert vllm_idx_check, (
f"Indices mismatch at seq_length {seq_length}, dtype {dtype}, "
f"params {params}, num_fused_shared_experts {num_fused_shared_experts}"
)
assert vllm_output_check, (
f"Output mismatch at seq_length {seq_length}, dtype {dtype}, "
f"params {params}, num_fused_shared_experts {num_fused_shared_experts}"
)
+46
View File
@@ -1912,6 +1912,52 @@ def moe_lora_align_block_size(
) )
def moe_fused_gate(
input_tensor: torch.Tensor,
bias: torch.Tensor,
num_expert_group: int,
topk_group: int,
topk: int,
num_fused_shared_experts: int = 0,
routed_scaling_factor: float = 0.0,
apply_routed_scaling_factor_on_output: bool = False,
) -> tuple[torch.Tensor, torch.Tensor]:
return torch.ops._moe_C.moe_fused_gate(
input_tensor,
bias,
num_expert_group,
topk_group,
topk,
num_fused_shared_experts,
routed_scaling_factor,
apply_routed_scaling_factor_on_output,
)
if hasattr(torch.ops._moe_C, "moe_fused_gate"):
@register_fake("_moe_C::moe_fused_gate")
def _moe_fused_gate_fake(
input_tensor: torch.Tensor,
bias: torch.Tensor,
num_expert_group: int,
topk_group: int,
topk: int,
num_fused_shared_experts: int = 0,
routed_scaling_factor: float = 1.0,
apply_routed_scaling_factor_on_output: bool = False,
) -> tuple[torch.Tensor, torch.Tensor]:
return torch.empty(
(input_tensor.size(0), topk),
dtype=torch.float32,
device=input_tensor.device,
), torch.empty(
(input_tensor.size(0), topk),
dtype=torch.int32,
device=input_tensor.device,
)
def moe_wna16_gemm( def moe_wna16_gemm(
input: torch.Tensor, input: torch.Tensor,
output: torch.Tensor, output: torch.Tensor,
+16
View File
@@ -231,6 +231,9 @@ if TYPE_CHECKING:
VLLM_DISABLE_SHARED_EXPERTS_STREAM: bool = False VLLM_DISABLE_SHARED_EXPERTS_STREAM: bool = False
VLLM_SHARED_EXPERTS_STREAM_TOKEN_THRESHOLD: int = 256 VLLM_SHARED_EXPERTS_STREAM_TOKEN_THRESHOLD: int = 256
VLLM_COMPILE_CACHE_SAVE_FORMAT: Literal["binary", "unpacked"] = "binary" VLLM_COMPILE_CACHE_SAVE_FORMAT: Literal["binary", "unpacked"] = "binary"
VLLM_FLAT_LOGPROBS: bool = False
VLLM_USE_CUDA_FUSION_SHARED_EXPERTS: bool = False
VLLM_USE_FUSED_MOE_ROUTER: bool = False
def get_default_cache_root(): def get_default_cache_root():
@@ -1522,6 +1525,19 @@ environment_variables: dict[str, Callable[[], Any]] = {
"VLLM_COMPILE_CACHE_SAVE_FORMAT": env_with_choices( "VLLM_COMPILE_CACHE_SAVE_FORMAT": env_with_choices(
"VLLM_COMPILE_CACHE_SAVE_FORMAT", "binary", ["binary", "unpacked"] "VLLM_COMPILE_CACHE_SAVE_FORMAT", "binary", ["binary", "unpacked"]
), ),
# Flag to enable FlatLogprobs whose GC overhead is significantly smaller than
# the original list[dict[int, Logprob]] approach.
# After enabled, PromptLogprobs and SampleLogprobs would populated as
# FlatLogprobs.
"VLLM_FLAT_LOGPROBS": lambda: bool(int(os.getenv("VLLM_FLAT_LOGPROBS", "0"))),
# Enable the fusion of the shared experts of the model with other experts.
"VLLM_USE_CUDA_FUSION_SHARED_EXPERTS": lambda: bool(
int(os.getenv("VLLM_USE_CUDA_FUSION_SHARED_EXPERTS", "0"))
),
# Use the fused grouped top-k MoE expert selection router
"VLLM_USE_FUSED_MOE_ROUTER": lambda: bool(
int(os.getenv("VLLM_USE_FUSED_MOE_ROUTER", "0"))
),
} }
# --8<-- [end:env-vars-definition] # --8<-- [end:env-vars-definition]
@@ -0,0 +1,146 @@
{
"1": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 5
},
"2": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 4
},
"4": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 64,
"num_warps": 4,
"num_stages": 4
},
"8": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 4
},
"16": {
"BLOCK_SIZE_M": 1,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 1,
"num_warps": 4,
"num_stages": 3
},
"24": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 4
},
"32": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 5
},
"48": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 64,
"num_warps": 4,
"num_stages": 3
},
"64": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 64,
"num_warps": 4,
"num_stages": 3
},
"96": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 3
},
"128": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 32,
"num_warps": 4,
"num_stages": 3
},
"256": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 32,
"num_warps": 4,
"num_stages": 3
},
"512": {
"BLOCK_SIZE_M": 16,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 3
},
"1024": {
"BLOCK_SIZE_M": 64,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 32,
"num_warps": 4,
"num_stages": 4
},
"1536": {
"BLOCK_SIZE_M": 64,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 32,
"num_warps": 4,
"num_stages": 4
},
"2048": {
"BLOCK_SIZE_M": 64,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 32,
"num_warps": 4,
"num_stages": 4
},
"3072": {
"BLOCK_SIZE_M": 64,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 32,
"num_warps": 4,
"num_stages": 4
},
"4096": {
"BLOCK_SIZE_M": 64,
"BLOCK_SIZE_N": 128,
"BLOCK_SIZE_K": 128,
"GROUP_SIZE_M": 16,
"num_warps": 4,
"num_stages": 3
}
}
@@ -1166,9 +1166,20 @@ def grouped_topk(
scoring_func: str = "softmax", scoring_func: str = "softmax",
routed_scaling_factor: float = 1.0, routed_scaling_factor: float = 1.0,
e_score_correction_bias: torch.Tensor | None = None, e_score_correction_bias: torch.Tensor | None = None,
num_fused_shared_experts: int = 0,
) -> tuple[torch.Tensor, torch.Tensor]: ) -> tuple[torch.Tensor, torch.Tensor]:
use_fused_moe_grouped_topk = envs.VLLM_USE_FUSED_MOE_GROUPED_TOPK
enable_fused_shared_experts = num_fused_shared_experts > 0
if enable_fused_shared_experts and use_fused_moe_grouped_topk:
logger.info(
"Fused MoE grouped topk is enabled with fused shared experts.",
"Only one of these options can be used at a time",
"Fused MoE grouped topk is disabled.",
)
use_fused_moe_grouped_topk = False
if ( if (
envs.VLLM_USE_FUSED_MOE_GROUPED_TOPK use_fused_moe_grouped_topk
and current_platform.is_cuda() and current_platform.is_cuda()
and num_expert_group <= 32 and num_expert_group <= 32
and topk <= 32 and topk <= 32
@@ -1196,6 +1207,7 @@ def grouped_topk(
raise ValueError(f"Unsupported scoring function: {scoring_func}") raise ValueError(f"Unsupported scoring function: {scoring_func}")
num_token = scores.size(0) num_token = scores.size(0)
num_experts = scores.size(1)
if e_score_correction_bias is not None: if e_score_correction_bias is not None:
# Store original scores before applying correction bias. We use biased # Store original scores before applying correction bias. We use biased
# scores for expert selection but original scores for routing weights # scores for expert selection but original scores for routing weights
@@ -1224,18 +1236,45 @@ def grouped_topk(
tmp_scores = scores.masked_fill(~score_mask.bool(), float("-inf")) # [n, e] tmp_scores = scores.masked_fill(~score_mask.bool(), float("-inf")) # [n, e]
if e_score_correction_bias is not None: if e_score_correction_bias is not None:
topk_ids = torch.topk(tmp_scores, k=topk, dim=-1, sorted=use_sorted)[1] topk_ids = torch.topk(
tmp_scores,
k=topk,
dim=-1,
sorted=(use_sorted or enable_fused_shared_experts),
)[1]
# Use original unbiased scores for the routing weights # Use original unbiased scores for the routing weights
topk_weights = original_scores.gather(1, topk_ids) topk_weights = original_scores.gather(1, topk_ids)
else: else:
topk_weights, topk_ids = torch.topk( topk_weights, topk_ids = torch.topk(
tmp_scores, k=topk, dim=-1, sorted=use_sorted tmp_scores,
k=topk,
dim=-1,
sorted=(use_sorted or enable_fused_shared_experts),
) )
if renormalize: if enable_fused_shared_experts:
topk_weights = topk_weights / topk_weights.sum(dim=-1, keepdim=True) assert routed_scaling_factor is not None, "With num_fused_shared_experts>0"
", routed_scaling_factor need to be provided"
topk_ids[:, -1] = torch.randint(
low=num_experts,
high=num_experts + num_fused_shared_experts,
size=(topk_ids.size(0),),
dtype=topk_ids.dtype,
device=topk_ids.device,
)
if routed_scaling_factor != 1.0:
topk_weights[:, -1] = (
topk_weights[:, :-1].sum(dim=-1) / routed_scaling_factor
)
if routed_scaling_factor != 1.0: if renormalize:
if not enable_fused_shared_experts:
topk_weights_sum = topk_weights.sum(dim=-1, keepdim=True)
else:
topk_weights_sum = topk_weights[:, :-1].sum(dim=-1, keepdim=True)
topk_weights = topk_weights / topk_weights_sum
if not enable_fused_shared_experts and routed_scaling_factor != 1.0:
topk_weights = topk_weights * routed_scaling_factor topk_weights = topk_weights * routed_scaling_factor
return topk_weights.to(torch.float32), topk_ids.to(torch.int32) return topk_weights.to(torch.float32), topk_ids.to(torch.int32)
+73 -23
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import math
from collections.abc import Callable, Iterable from collections.abc import Callable, Iterable
from contextlib import nullcontext from contextlib import nullcontext
from enum import Enum from enum import Enum
@@ -57,6 +58,8 @@ from vllm.utils.torch_utils import (
from vllm.v1.worker.ubatching import dbo_current_ubatch_id from vllm.v1.worker.ubatching import dbo_current_ubatch_id
if current_platform.is_cuda_alike(): if current_platform.is_cuda_alike():
from vllm._custom_ops import moe_fused_gate
from .fused_moe import eplb_map_to_physical_and_record, fused_experts from .fused_moe import eplb_map_to_physical_and_record, fused_experts
else: else:
fused_experts = None # type: ignore fused_experts = None # type: ignore
@@ -96,6 +99,10 @@ from vllm.model_executor.layers.fused_moe.unquantized_fused_moe_method import (
logger = init_logger(__name__) logger = init_logger(__name__)
def is_power_of_two(n):
return n > 0 and math.log2(n).is_integer()
class FusedMoeWeightScaleSupported(Enum): class FusedMoeWeightScaleSupported(Enum):
TENSOR = "tensor" TENSOR = "tensor"
CHANNEL = "channel" CHANNEL = "channel"
@@ -410,6 +417,11 @@ class FusedMoE(CustomOp):
dp_size_=dp_size_, dp_size_=dp_size_,
vllm_parallel_config=vllm_config.parallel_config, vllm_parallel_config=vllm_config.parallel_config,
) )
enable_fused_shared_experts = envs.VLLM_USE_CUDA_FUSION_SHARED_EXPERTS
if enable_fused_shared_experts:
assert n_shared_experts is not None
num_experts += n_shared_experts
top_k += n_shared_experts
self.global_num_experts = num_experts + num_redundant_experts self.global_num_experts = num_experts + num_redundant_experts
self.logical_num_experts = num_experts self.logical_num_experts = num_experts
@@ -443,7 +455,7 @@ class FusedMoE(CustomOp):
vllm_config.parallel_config.expert_placement_strategy vllm_config.parallel_config.expert_placement_strategy
) )
# ROCm aiter shared experts fusion # ROCm aiter and CUDA shared experts fusion
self.rocm_aiter_fmoe_enabled = rocm_aiter_ops.is_fused_moe_enabled() self.rocm_aiter_fmoe_enabled = rocm_aiter_ops.is_fused_moe_enabled()
self.aiter_fmoe_shared_expert_enabled = ( self.aiter_fmoe_shared_expert_enabled = (
rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() rocm_aiter_ops.is_fusion_moe_shared_experts_enabled()
@@ -451,16 +463,19 @@ class FusedMoE(CustomOp):
self.num_fused_shared_experts = ( self.num_fused_shared_experts = (
n_shared_experts n_shared_experts
if n_shared_experts is not None and self.aiter_fmoe_shared_expert_enabled if n_shared_experts is not None
and (self.aiter_fmoe_shared_expert_enabled or enable_fused_shared_experts)
else 0 else 0
) )
if ( if (
not self.aiter_fmoe_shared_expert_enabled not self.aiter_fmoe_shared_expert_enabled
and not enable_fused_shared_experts
and self.num_fused_shared_experts != 0 and self.num_fused_shared_experts != 0
): ):
raise ValueError( raise ValueError(
"n_shared_experts is only supported on ROCm aiter when " "n_shared_experts is only supported on ROCm aiter when "
"VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS is enabled" "VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS is enabled"
"and on CUDA when VLLM_USE_CUDA_FUSION_SHARED_EXPERTS is enabled"
) )
# Determine expert maps # Determine expert maps
@@ -508,6 +523,15 @@ class FusedMoE(CustomOp):
self.global_num_experts, self.global_num_experts,
get_compressed_expert_map(self.expert_map), get_compressed_expert_map(self.expert_map),
) )
if self.num_fused_shared_experts > 0:
logger.warning(
"With EP enabled and share expert fusion enabled"
", share expert replica should be same as ep_size"
"got share expert replica = %d"
"and ep_size = %d",
self.num_fused_shared_experts,
self.ep_size,
)
else: else:
self.local_num_experts, self.expert_map, self.expert_mask = ( self.local_num_experts, self.expert_map, self.expert_mask = (
self.global_num_experts, self.global_num_experts,
@@ -516,10 +540,10 @@ class FusedMoE(CustomOp):
) )
self.top_k = top_k self.top_k = top_k
if self.aiter_fmoe_shared_expert_enabled:
self._init_aiter_shared_experts_topK_buffer( self._init_aiter_shared_experts_topK_buffer(
vllm_config=vllm_config, dp_size=dp_size_ vllm_config=vllm_config, dp_size=dp_size_
) )
assert intermediate_size % self.tp_size == 0 assert intermediate_size % self.tp_size == 0
self.hidden_size = hidden_size self.hidden_size = hidden_size
@@ -1487,27 +1511,53 @@ class FusedMoE(CustomOp):
elif use_grouped_topk: elif use_grouped_topk:
assert topk_group is not None assert topk_group is not None
assert num_expert_group is not None assert num_expert_group is not None
if rocm_aiter_ops.is_fused_moe_enabled(): if hidden_states.shape[0] == 0:
topk_ids = torch.full(
(0, top_k), -1, dtype=torch.int, device=hidden_states.device
)
topk_weights = torch.empty(
(0, top_k), dtype=torch.float32, device=hidden_states.device
)
elif rocm_aiter_ops.is_fused_moe_enabled():
if not rocm_aiter_ops.is_fusion_moe_shared_experts_enabled(): if not rocm_aiter_ops.is_fusion_moe_shared_experts_enabled():
assert num_fused_shared_experts == 0 assert num_fused_shared_experts == 0
grouped_topk_impl = partial( grouped_topk_impl = rocm_aiter_grouped_topk
rocm_aiter_grouped_topk,
num_fused_shared_experts=num_fused_shared_experts,
)
else: else:
grouped_topk_impl = grouped_topk grouped_topk_impl = grouped_topk
topk_weights, topk_ids = grouped_topk_impl( if (
hidden_states=hidden_states, envs.VLLM_USE_FUSED_MOE_ROUTER
gating_output=router_logits, and e_score_correction_bias is not None
topk=top_k, and is_power_of_two(e_score_correction_bias.shape[0])
renormalize=renormalize, ):
num_expert_group=num_expert_group, # The fused kernel can only work with 128/256 experts
topk_group=topk_group, topk_weights, topk_ids = moe_fused_gate(
scoring_func=scoring_func, input_tensor=router_logits.to(dtype=torch.float32),
routed_scaling_factor=routed_scaling_factor, bias=e_score_correction_bias.data.to(dtype=torch.float32),
e_score_correction_bias=e_score_correction_bias, num_expert_group=num_expert_group,
) topk_group=topk_group,
topk=top_k,
num_fused_shared_experts=num_fused_shared_experts,
routed_scaling_factor=routed_scaling_factor
if routed_scaling_factor is not None
else 1.0,
apply_routed_scaling_factor_on_output=False,
)
else:
topk_weights, topk_ids = grouped_topk_impl(
hidden_states=hidden_states,
gating_output=router_logits,
topk=top_k,
renormalize=renormalize,
num_expert_group=num_expert_group,
topk_group=topk_group,
scoring_func=scoring_func,
routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias,
num_fused_shared_experts=num_fused_shared_experts,
)
if indices_type is not None:
topk_ids = topk_ids.to(dtype=indices_type)
elif e_score_correction_bias is not None: elif e_score_correction_bias is not None:
topk_weights, topk_ids = fused_topk_bias( topk_weights, topk_ids = fused_topk_bias(
hidden_states=hidden_states, hidden_states=hidden_states,
@@ -1959,7 +2009,7 @@ class FusedMoE(CustomOp):
return states return states
if self.shared_experts is not None: if self.shared_experts is not None and self.num_fused_shared_experts == 0:
return ( return (
final_hidden_states[0], final_hidden_states[0],
combine_output(final_hidden_states[1]), combine_output(final_hidden_states[1]),
@@ -316,6 +316,7 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
expert_load_view=expert_load_view, expert_load_view=expert_load_view,
logical_to_physical_map=logical_to_physical_map, logical_to_physical_map=logical_to_physical_map,
logical_replica_count=logical_replica_count, logical_replica_count=logical_replica_count,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
def get_fused_moe_quant_config( def get_fused_moe_quant_config(
@@ -636,6 +636,7 @@ class AWQMoEMethod(FusedMoEMethodBase):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
return fused_marlin_moe( return fused_marlin_moe(
@@ -465,6 +465,7 @@ class CompressedTensorsW4A4MoeMethod(CompressedTensorsMoEMethod):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
if self.use_marlin: if self.use_marlin:
@@ -1316,6 +1317,7 @@ class CompressedTensorsW8A8Int8MoEMethod(CompressedTensorsMoEMethod):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
return fused_experts( return fused_experts(
@@ -1677,6 +1679,7 @@ class CompressedTensorsWNA16MarlinMoEMethod(CompressedTensorsMoEMethod):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
return fused_marlin_moe( return fused_marlin_moe(
@@ -178,6 +178,7 @@ class ExpertsInt8MoEMethod(FusedMoEMethodBase):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
return fused_experts( return fused_experts(
@@ -665,6 +665,7 @@ class GGUFMoEMethod(FusedMoEMethodBase):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
return fused_moe_gguf( return fused_moe_gguf(
x, x,
@@ -763,6 +763,7 @@ class GPTQMarlinMoEMethod(FusedMoEMethodBase):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
return fused_marlin_moe( return fused_marlin_moe(
@@ -752,6 +752,7 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
if self.flashinfer_moe_backend == FlashinferMoeBackend.CUTLASS: if self.flashinfer_moe_backend == FlashinferMoeBackend.CUTLASS:
@@ -1690,6 +1691,7 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
if self.use_marlin: if self.use_marlin:
@@ -399,6 +399,7 @@ class MoeWNA16Method(FusedMoEMethodBase):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
return fused_experts( return fused_experts(
@@ -373,6 +373,7 @@ class QuarkW8A8Fp8MoEMethod(QuarkMoEMethod):
routed_scaling_factor=routed_scaling_factor, routed_scaling_factor=routed_scaling_factor,
e_score_correction_bias=e_score_correction_bias, e_score_correction_bias=e_score_correction_bias,
indices_type=self.topk_indices_dtype, indices_type=self.topk_indices_dtype,
num_fused_shared_experts=layer.num_fused_shared_experts,
) )
if self.rocm_aiter_moe_enabled: if self.rocm_aiter_moe_enabled:
+28 -13
View File
@@ -98,6 +98,8 @@ if current_platform.is_cuda_alike():
elif current_platform.is_xpu(): elif current_platform.is_xpu():
from vllm._ipex_ops import ipex_ops as ops from vllm._ipex_ops import ipex_ops as ops
import vllm.envs as envs
logger = init_logger(__name__) logger = init_logger(__name__)
@@ -277,6 +279,7 @@ class DeepseekV2MoE(nn.Module):
self.enable_eplb = parallel_config.enable_eplb self.enable_eplb = parallel_config.enable_eplb
self.n_redundant_experts = eplb_config.num_redundant_experts self.n_redundant_experts = eplb_config.num_redundant_experts
self.n_logical_experts = self.n_routed_experts self.n_logical_experts = self.n_routed_experts
self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
self.n_local_physical_experts = self.n_physical_experts // self.ep_size self.n_local_physical_experts = self.n_physical_experts // self.ep_size
@@ -287,10 +290,12 @@ class DeepseekV2MoE(nn.Module):
) )
self.is_rocm_aiter_moe_enabled = rocm_aiter_ops.is_fused_moe_enabled() self.is_rocm_aiter_moe_enabled = rocm_aiter_ops.is_fused_moe_enabled()
self.is_fusion_moe_shared_experts_enabled = ( enable_fused_shared_experts = envs.VLLM_USE_CUDA_FUSION_SHARED_EXPERTS
rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() if (
) config.n_shared_experts is None
if config.n_shared_experts is None or self.is_fusion_moe_shared_experts_enabled: or self.is_rocm_aiter_moe_enabled
or enable_fused_shared_experts
):
self.shared_experts = None self.shared_experts = None
else: else:
intermediate_size = config.moe_intermediate_size * config.n_shared_experts intermediate_size = config.moe_intermediate_size * config.n_shared_experts
@@ -304,7 +309,11 @@ class DeepseekV2MoE(nn.Module):
reduce_results=False, reduce_results=False,
prefix=f"{prefix}.shared_experts", prefix=f"{prefix}.shared_experts",
) )
used_inside_scaling = (
self.is_rocm_aiter_moe_enabled
or envs.VLLM_USE_FUSED_MOE_ROUTER
or enable_fused_shared_experts
)
self.experts = SharedFusedMoE( self.experts = SharedFusedMoE(
shared_experts=self.shared_experts, shared_experts=self.shared_experts,
gate=self.gate, gate=self.gate,
@@ -323,14 +332,15 @@ class DeepseekV2MoE(nn.Module):
# we do scaling outside, set factor to 1.0 to avoid double mul # we do scaling outside, set factor to 1.0 to avoid double mul
# aiter applies routed_scaling_factor internally # aiter applies routed_scaling_factor internally
routed_scaling_factor=1.0 routed_scaling_factor=1.0
if not self.is_rocm_aiter_moe_enabled if not used_inside_scaling
else self.routed_scaling_factor, else self.routed_scaling_factor,
e_score_correction_bias=self.gate.e_score_correction_bias, e_score_correction_bias=self.gate.e_score_correction_bias,
enable_eplb=self.enable_eplb, enable_eplb=self.enable_eplb,
num_redundant_experts=self.n_redundant_experts, num_redundant_experts=self.n_redundant_experts,
is_sequence_parallel=self.is_sequence_parallel, is_sequence_parallel=self.is_sequence_parallel,
n_shared_experts=config.n_shared_experts n_shared_experts=config.n_shared_experts
if self.is_fusion_moe_shared_experts_enabled if rocm_aiter_ops.is_fusion_moe_shared_experts_enabled()
or enable_fused_shared_experts
else None, else None,
) )
@@ -1435,6 +1445,7 @@ class DeepseekV2ForCausalLM(
rocm_aiter_moe_shared_expert_enabled = ( rocm_aiter_moe_shared_expert_enabled = (
rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() rocm_aiter_ops.is_fusion_moe_shared_experts_enabled()
) )
enable_fused_shared_experts = envs.VLLM_USE_CUDA_FUSION_SHARED_EXPERTS
stacked_params_mapping = [ stacked_params_mapping = [
# (param_name, shard_name, shard_id) # (param_name, shard_name, shard_id)
("gate_up_proj", "gate_proj", 0), ("gate_up_proj", "gate_proj", 0),
@@ -1454,8 +1465,12 @@ class DeepseekV2ForCausalLM(
else: else:
stacked_params_mapping.extend(mla_params_mapping) stacked_params_mapping.extend(mla_params_mapping)
# Params for weights, fp8 weight scales, fp8 activation scales if enable_fused_shared_experts:
# (param_name, weight_name, expert_id, shard_id) logger.info(
"Cloning %s replicas of the shared expert into MoE",
self.num_shared_experts,
)
expert_params_mapping = SharedFusedMoE.make_expert_params_mapping( expert_params_mapping = SharedFusedMoE.make_expert_params_mapping(
ckpt_gate_proj_name="gate_proj", ckpt_gate_proj_name="gate_proj",
ckpt_down_proj_name="down_proj", ckpt_down_proj_name="down_proj",
@@ -1463,7 +1478,7 @@ class DeepseekV2ForCausalLM(
num_experts=self.config.n_routed_experts num_experts=self.config.n_routed_experts
+ ( + (
self.config.n_shared_experts self.config.n_shared_experts
if rocm_aiter_moe_shared_expert_enabled if rocm_aiter_moe_shared_expert_enabled or enable_fused_shared_experts
else 0 else 0
), ),
num_redundant_experts=self.num_redundant_experts, num_redundant_experts=self.num_redundant_experts,
@@ -1479,9 +1494,9 @@ class DeepseekV2ForCausalLM(
if spec_layer is not None: if spec_layer is not None:
continue # skip spec decode layers for main model continue # skip spec decode layers for main model
is_fuse_shared_experts_layer = rocm_aiter_moe_shared_expert_enabled and ( is_fuse_shared_experts_layer = (
"mlp.shared_experts" in name enable_fused_shared_experts or rocm_aiter_moe_shared_expert_enabled
) ) and ("mlp.shared_experts" in name)
for param_name, weight_name, shard_id in stacked_params_mapping: for param_name, weight_name, shard_id in stacked_params_mapping:
# Skip non-stacked layers and experts (experts handled below). # Skip non-stacked layers and experts (experts handled below).