Skip to content

Commit 552f462

Browse files
authored
[None][feat] Optimize trtllmgen moe routing (#15656)
Signed-off-by: jiahanc <173873397+jiahanc@users.noreply.github.com>
1 parent 0bba015 commit 552f462

3 files changed

Lines changed: 411 additions & 60 deletions

File tree

cpp/tensorrt_llm/kernels/trtllmGenKernels/blockScaleMoe/routing/RoutingCustom.cu

Lines changed: 264 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -709,23 +709,78 @@ void launchDynBlockKernel(Data const& data, uint32_t numThreadsHist, void* strea
709709
//
710710
////////////////////////////////////////////////////////////////////////////////////////////////////
711711

712-
template <typename KernelParams>
713-
#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900))
714-
__global__ void __cluster_dims__(NumBlocksPerCluster, 1, 1) __launch_bounds__(NumThreads)
715-
routingIndicesClusterKernel(KernelParams params)
712+
static constexpr int ClusterBlockDim256 = NumExperts256Experts;
713+
static constexpr int ClusterBlockDim512 = NumExperts512Experts;
714+
static constexpr int ClusterBlockDim1024 = NumThreads;
715+
static constexpr int MaxNumTokensClusterScores256 = NumBlocksPerCluster * (ClusterBlockDim256 / WarpSize);
716+
static constexpr int MaxNumTokensClusterScores512 = NumBlocksPerCluster * (ClusterBlockDim512 / WarpSize);
717+
718+
template <typename TierT, typename TierListT>
719+
struct PrependTier;
720+
721+
template <typename TierT, typename... Tiers>
722+
struct PrependTier<TierT, TierList<Tiers...>>
723+
{
724+
using type = TierList<TierT, Tiers...>;
725+
};
726+
727+
template <int ClusterBlockDim, typename TierListT>
728+
struct FilterClusterTiers;
729+
730+
template <int ClusterBlockDim>
731+
struct FilterClusterTiers<ClusterBlockDim, TierList<>>
732+
{
733+
using type = TierList<>;
734+
};
735+
736+
template <int ClusterBlockDim, typename First, typename... Rest>
737+
struct FilterClusterTiers<ClusterBlockDim, TierList<First, Rest...>>
738+
{
739+
using Tail = typename FilterClusterTiers<ClusterBlockDim, TierList<Rest...>>::type;
740+
static constexpr bool IsValid = First::kExperts <= ClusterBlockDim || First::kExperts % ClusterBlockDim == 0;
741+
using type = std::conditional_t<IsValid, typename PrependTier<First, Tail>::type, Tail>;
742+
};
743+
744+
template <int ClusterBlockDim, typename PreProc, typename PostProc>
745+
struct ClusterPolicyTraits
746+
{
747+
using Pairs = typename FilterClusterTiers<ClusterBlockDim, typename PolicyTraits<PreProc, PostProc>::Pairs>::type;
748+
};
749+
750+
template <>
751+
struct ClusterPolicyTraits<ClusterBlockDim1024, NoOpPreprocess, SoftmaxPostprocess>
752+
{
753+
using Pairs = TierList<Tier<128, 4>, Tier<128, 8>, Tier<160, 8>, Tier<256, 8>, Tier<256, 16>, Tier<512, 8>,
754+
Tier<512, 16>, Tier<512, 22>, Tier<512, 32>, Tier<576, 8>, Tier<768, 32>, Tier<1024, 32>, Tier<2048, 32>>;
755+
};
756+
757+
template <>
758+
struct ClusterPolicyTraits<ClusterBlockDim512, NoOpPreprocess, SoftmaxPostprocess>
759+
{
760+
using Pairs = TierList<Tier<128, 4>, Tier<128, 8>, Tier<160, 8>, Tier<256, 8>, Tier<256, 16>, Tier<512, 8>,
761+
Tier<512, 16>, Tier<512, 22>, Tier<512, 32>, Tier<1024, 32>, Tier<1536, 32>, Tier<2048, 32>>;
762+
};
763+
764+
template <>
765+
struct ClusterPolicyTraits<ClusterBlockDim256, NoOpPreprocess, SoftmaxPostprocess>
766+
{
767+
using Pairs = TierList<Tier<128, 4>, Tier<128, 8>, Tier<160, 8>, Tier<256, 8>, Tier<256, 16>, Tier<512, 8>,
768+
Tier<512, 16>, Tier<512, 22>, Tier<512, 32>, Tier<768, 32>, Tier<1024, 32>, Tier<1536, 32>, Tier<2048, 32>>;
769+
};
770+
771+
template <typename KernelParams, typename BaseType, int ClusterBlockDim, int ClusterNumWarps>
772+
__device__ __forceinline__ void routingIndicesClusterKernelBody(
773+
KernelParams params, PackedScoreIdx<BaseType>* smemPackedScoreIdx)
716774
{
717775
using OutputT = typename KernelParams::OutputT;
718776
using InputT = typename KernelParams::InputT;
719-
using BaseType = typename KernelParams::ExpertSelectPolicy::template BaseType<InputT>;
720777
using TypePacked = PackedScoreIdx<BaseType>;
721778
static constexpr int VecSize = KernelParams::MaxNumExperts / WarpSize;
722779

723-
__shared__ TypePacked __attribute((aligned(128))) smemPackedScoreIdx[NumWarps * KernelParams::MaxNumTopExperts];
724-
725780
uint32_t const clusterBlockRank = blockIdx.x;
726781
int32_t const warpIdx = __shfl_sync(0xffffffff, threadIdx.x / WarpSize, 0);
727782
int32_t const laneIdx = cutlass::arch::LaneId();
728-
auto warpTokenIdx = clusterBlockRank * NumWarps + warpIdx;
783+
auto warpTokenIdx = clusterBlockRank * ClusterNumWarps + warpIdx;
729784
auto scoreOffset = warpTokenIdx * params.mNumExperts;
730785
bool validToken = warpTokenIdx < params.mNumTokens;
731786
auto block = cg::this_thread_block();
@@ -758,24 +813,136 @@ __global__ void __cluster_dims__(NumBlocksPerCluster, 1, 1) __launch_bounds__(Nu
758813

759814
if (params.mPtrScores != nullptr)
760815
{
761-
routingPermutation<KernelParams, BaseType, NumThreads, NumWarps, KernelParams::MaxNumTopExperts,
816+
routingPermutation<KernelParams, BaseType, ClusterBlockDim, ClusterNumWarps, KernelParams::MaxNumTopExperts,
762817
/*LoadExpertIdxFromGlobal=*/false>(params, smemPackedScoreIdx, warpIdx, clusterBlockRank);
763818
}
764819
else
765820
{
766-
routingPermutation<KernelParams, BaseType, NumThreads, NumWarps, KernelParams::MaxNumTopExperts,
821+
routingPermutation<KernelParams, BaseType, ClusterBlockDim, ClusterNumWarps, KernelParams::MaxNumTopExperts,
767822
/*LoadExpertIdxFromGlobal=*/true>(params, smemPackedScoreIdx, warpIdx, clusterBlockRank);
768823
}
769824
}
825+
826+
template <typename KernelParams, int ClusterBlockDim = NumThreads>
827+
#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900))
828+
__global__ void __cluster_dims__(NumBlocksPerCluster, 1, 1) __launch_bounds__(ClusterBlockDim)
829+
routingIndicesClusterKernel(KernelParams params)
830+
{
831+
using InputT = typename KernelParams::InputT;
832+
using BaseType = typename KernelParams::ExpertSelectPolicy::template BaseType<InputT>;
833+
using TypePacked = PackedScoreIdx<BaseType>;
834+
static constexpr int NumWarpsBlock = ClusterBlockDim / WarpSize;
835+
static_assert(ClusterBlockDim % WarpSize == 0);
836+
static_assert(ClusterBlockDim <= NumThreads);
837+
__shared__ TypePacked __attribute((aligned(128)))
838+
smemPackedScoreIdx[NumWarpsBlock * KernelParams::MaxNumTopExperts];
839+
routingIndicesClusterKernelBody<KernelParams, BaseType, ClusterBlockDim, NumWarpsBlock>(params, smemPackedScoreIdx);
840+
}
770841
#else
771-
__global__ void __launch_bounds__(NumThreads) routingIndicesClusterKernel(KernelParams /* params */)
842+
__global__ void __launch_bounds__(ClusterBlockDim) routingIndicesClusterKernel(KernelParams /* params */)
772843
{
773844
assert(false && "routingIndicesClusterKernel is only supported on SM90+ architectures");
774845
}
775846
#endif // if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900))
776847

848+
template <typename KernelParams, int ClusterBlockDim>
849+
void launchClusterKernelInstance(Data const& data, void* stream)
850+
{
851+
static_assert(ClusterBlockDim % WarpSize == 0);
852+
static_assert(ClusterBlockDim <= NumThreads);
853+
854+
cudaLaunchConfig_t config{};
855+
config.gridDim = NumBlocksPerCluster;
856+
config.blockDim = ClusterBlockDim;
857+
config.dynamicSmemBytes = 0;
858+
config.stream = (cudaStream_t) stream;
859+
860+
cudaLaunchAttribute attributes[2] = {};
861+
attributes[0].id = cudaLaunchAttributeProgrammaticStreamSerialization;
862+
attributes[0].val.programmaticStreamSerializationAllowed = int(data.mUsePdl);
863+
attributes[1].id = cudaLaunchAttributeCooperative;
864+
attributes[1].val.cooperative = 0;
865+
config.attrs = attributes;
866+
config.numAttrs = 2;
867+
868+
auto params = KernelParams::setKernelParams(data);
869+
auto kernelTyped = routingIndicesClusterKernel<KernelParams, ClusterBlockDim>;
870+
TLLM_CUDA_CHECK(cudaLaunchKernelEx(&config, kernelTyped, params));
871+
}
872+
873+
template <int ClusterBlockDim, typename PreProc, typename PostProc, int MaxNumExperts, int MaxNumTopExperts>
874+
void launchClusterKernelForTier(Data const& data, void* stream)
875+
{
876+
using ExpertSelect = TopKExpertSelect<PreProc, PostProc>;
877+
if (data.mDtypeOutput == tg::Dtype::Fp32)
878+
{
879+
using ParamsT = KernelParams<float, float, MaxNumExperts, MaxNumTopExperts, ExpertSelect>;
880+
launchClusterKernelInstance<ParamsT, ClusterBlockDim>(data, stream);
881+
}
882+
else if (data.mDtypeOutput == tg::Dtype::Bfloat16 && data.mDtypeInput == tg::Dtype::Fp32)
883+
{
884+
using ParamsT = KernelParams<float, __nv_bfloat16, MaxNumExperts, MaxNumTopExperts, ExpertSelect>;
885+
launchClusterKernelInstance<ParamsT, ClusterBlockDim>(data, stream);
886+
}
887+
else if (data.mDtypeOutput == tg::Dtype::Bfloat16 && data.mDtypeInput == tg::Dtype::Bfloat16)
888+
{
889+
using ParamsT = KernelParams<__nv_bfloat16, __nv_bfloat16, MaxNumExperts, MaxNumTopExperts, ExpertSelect>;
890+
launchClusterKernelInstance<ParamsT, ClusterBlockDim>(data, stream);
891+
}
892+
else
893+
{
894+
TLLM_LOG_ERROR("Unsupported dtype combination: dtypeOutput=%d, dtypeInput=%d",
895+
static_cast<int>(data.mDtypeOutput), static_cast<int>(data.mDtypeInput));
896+
}
897+
}
898+
899+
template <int ClusterBlockDim, typename PreProc, typename PostProc>
900+
void launchClusterKernelForPolicy(Data const& data, void* stream)
901+
{
902+
using Pairs = typename ClusterPolicyTraits<ClusterBlockDim, PreProc, PostProc>::Pairs;
903+
bool dispatched = dispatchTierPairs(static_cast<Pairs*>(nullptr), data,
904+
[&](auto eTag, auto kTag)
905+
{
906+
launchClusterKernelForTier<ClusterBlockDim, PreProc, PostProc, decltype(eTag)::value,
907+
decltype(kTag)::value>(data, stream);
908+
});
909+
if (!dispatched)
910+
{
911+
TLLM_LOG_ERROR("No tier covers numExperts=%d topK=%d", data.mNumExperts, data.mTopK);
912+
}
913+
}
914+
915+
template <int ClusterBlockDim>
916+
void launchClusterKernelForBlockDim(Data const& data, void* stream)
917+
{
918+
dispatchRoutingPolicy(data,
919+
[&](auto preProc, auto postProc)
920+
{ launchClusterKernelForPolicy<ClusterBlockDim, decltype(preProc), decltype(postProc)>(data, stream); });
921+
}
922+
777923
void launchClusterKernel(Data const& data, void* stream)
778924
{
925+
// Each warp owns one token, so the reduced-thread cluster variants have lower token capacity.
926+
// Use them only where the requested token count fits; otherwise keep the original 1024-thread launch.
927+
if (data.mNumTokens <= MaxNumTokensClusterScores256)
928+
{
929+
launchClusterKernelForBlockDim<ClusterBlockDim256>(data, stream);
930+
return;
931+
}
932+
if (data.mNumTokens <= MaxNumTokensClusterScores512)
933+
{
934+
launchClusterKernelForBlockDim<ClusterBlockDim512>(data, stream);
935+
return;
936+
}
937+
938+
bool const useNoOpSoftmaxScores = data.mPtrScores != nullptr && data.mPreprocessType == RoutingPreprocessType::None
939+
&& data.mPostprocessType == RoutingPostprocessType::Softmax;
940+
if (useNoOpSoftmaxScores)
941+
{
942+
launchClusterKernelForPolicy<ClusterBlockDim1024, NoOpPreprocess, SoftmaxPostprocess>(data, stream);
943+
return;
944+
}
945+
779946
LAUNCH_ROUTING_CUSTOM(data, false, routingIndicesClusterKernel, NumBlocksPerCluster, NumThreads,
780947
/*smemSize=*/0, // No dynamic smem
781948
stream);
@@ -788,15 +955,74 @@ void launchClusterKernel(Data const& data, void* stream)
788955
//
789956
////////////////////////////////////////////////////////////////////////////////////////////////////
790957

958+
template <int MaxNumExperts, int MaxNumTopExperts>
959+
struct HistogramScoresLaunchConfig : DefaultRoutingLaunchConfig<MaxNumExperts, MaxNumTopExperts>
960+
{
961+
static constexpr int DefaultBlockDim = DefaultRoutingLaunchConfig<MaxNumExperts, MaxNumTopExperts>::BlockDim;
962+
static constexpr int HistogramScoresBlockDim = NumExperts256Experts;
963+
964+
// This kernel uses one warp per token and keeps per-warp arrays sized by both the expert tier and
965+
// the topK tier. The 256-expert tier already launches with 256 threads; for larger tiers, fewer
966+
// warps per CTA gives each thread more register headroom while preserving total warp-level
967+
// parallelism by scaling the grid cap below.
968+
static constexpr bool UseHistogramScoresBlockDim = DefaultBlockDim > HistogramScoresBlockDim;
969+
static constexpr int BlockDim = UseHistogramScoresBlockDim ? HistogramScoresBlockDim : DefaultBlockDim;
970+
971+
static_assert(BlockDim % WarpSize == 0);
972+
static_assert(BlockDim <= NumThreads);
973+
974+
static int blockDim(Data const& /*data*/, int /*numThreads*/)
975+
{
976+
return BlockDim;
977+
}
978+
979+
static int gridDim(Data const& data, int numBlocks, int /*blockDim*/)
980+
{
981+
if constexpr (UseHistogramScoresBlockDim)
982+
{
983+
static constexpr int NumWarpsBlock = BlockDim / WarpSize;
984+
static constexpr int MaxBlockScale = (DefaultBlockDim + BlockDim - 1) / BlockDim;
985+
int const tokenBlocks = (static_cast<int>(data.mNumTokens) + NumWarpsBlock - 1) / NumWarpsBlock;
986+
int const scaledMaxBlocks = numBlocks * MaxBlockScale;
987+
int const selectedBlocks = tokenBlocks < scaledMaxBlocks ? tokenBlocks : scaledMaxBlocks;
988+
return selectedBlocks > 0 ? selectedBlocks : 1;
989+
}
990+
else
991+
{
992+
return DefaultRoutingLaunchConfig<MaxNumExperts, MaxNumTopExperts>::gridDim(
993+
data, numBlocks, DefaultBlockDim);
994+
}
995+
}
996+
};
997+
998+
template <typename ExpertSelect, int MaxNumExperts, int MaxNumTopExperts>
999+
struct HistogramScoresKernelConfig : HistogramScoresLaunchConfig<MaxNumExperts, MaxNumTopExperts>
1000+
{
1001+
};
1002+
1003+
template <typename PreProc, typename PostProc>
1004+
struct HistogramScoresPolicyTraits : PolicyTraits<PreProc, PostProc>
1005+
{
1006+
};
1007+
1008+
template <>
1009+
struct HistogramScoresPolicyTraits<NoOpPreprocess, SoftmaxPostprocess>
1010+
{
1011+
using Pairs
1012+
= TierList<Tier<128, 4>, Tier<128, 8>, Tier<160, 8>, Tier<256, 8>, Tier<256, 16>, Tier<512, 8>, Tier<512, 16>,
1013+
Tier<512, 22>, Tier<512, 32>, Tier<576, 8>, Tier<768, 32>, Tier<1024, 32>, Tier<1536, 32>, Tier<2048, 32>>;
1014+
};
1015+
7911016
template <typename KernelParams>
792-
__global__ void __launch_bounds__(KernelParams::MaxNumExperts <= 1024 ? KernelParams::MaxNumExperts : 1024)
1017+
__global__ void __launch_bounds__(HistogramScoresKernelConfig<typename KernelParams::ExpertSelectPolicy,
1018+
KernelParams::MaxNumExperts, KernelParams::MaxNumTopExperts>::BlockDim)
7931019
routingIndicesHistogramScoresKernel(KernelParams params)
7941020
{
7951021
using OutputT = typename KernelParams::OutputT;
7961022
using InputT = typename KernelParams::InputT;
7971023
using BaseType = typename KernelParams::ExpertSelectPolicy::template BaseType<InputT>;
798-
// Cap actual thread count at 1024 when MaxNumExperts > 1024.
799-
static constexpr int NumThreadsBlock = KernelParams::MaxNumExperts <= 1024 ? KernelParams::MaxNumExperts : 1024;
1024+
static constexpr int NumThreadsBlock = HistogramScoresKernelConfig<typename KernelParams::ExpertSelectPolicy,
1025+
KernelParams::MaxNumExperts, KernelParams::MaxNumTopExperts>::BlockDim;
8001026

8011027
// VecSize stays based on MaxNumExperts — each warp still processes all experts for one token.
8021028
static constexpr int VecSize = KernelParams::MaxNumExperts / WarpSize;
@@ -855,9 +1081,30 @@ __global__ void __launch_bounds__(KernelParams::MaxNumExperts <= 1024 ? KernelPa
8551081

8561082
static void launchHistogramScoresKernel(Data const& data, uint32_t maxNumBlocks, uint32_t numThreadsHist, void* stream)
8571083
{
858-
LAUNCH_ROUTING_CUSTOM(data, false, routingIndicesHistogramScoresKernel, maxNumBlocks, numThreadsHist,
859-
/*smemSize=*/0, // No dynamic smem
860-
stream);
1084+
dispatchRoutingPolicy(data,
1085+
[&](auto preProc, auto postProc)
1086+
{
1087+
using PreProc = decltype(preProc);
1088+
using PostProc = decltype(postProc);
1089+
using Pairs = typename HistogramScoresPolicyTraits<PreProc, PostProc>::Pairs;
1090+
bool dispatched = dispatchTierPairs(static_cast<Pairs*>(nullptr), data,
1091+
[&](auto eTag, auto kTag)
1092+
{
1093+
using ExpertSelect = TopKExpertSelect<PreProc, PostProc>;
1094+
using LaunchConfig
1095+
= HistogramScoresKernelConfig<ExpertSelect, decltype(eTag)::value, decltype(kTag)::value>;
1096+
int const effectiveThreads = LaunchConfig::blockDim(data, static_cast<int>(numThreadsHist));
1097+
int const effectiveBlocks
1098+
= LaunchConfig::gridDim(data, static_cast<int>(maxNumBlocks), effectiveThreads);
1099+
LAUNCH_ROUTING_WITH_POLICIES(data, false, routingIndicesHistogramScoresKernel, effectiveBlocks,
1100+
effectiveThreads,
1101+
/*smemSize=*/0, stream, PreProc, PostProc, decltype(eTag)::value, decltype(kTag)::value);
1102+
});
1103+
if (!dispatched)
1104+
{
1105+
TLLM_LOG_ERROR("No tier covers numExperts=%d topK=%d", data.mNumExperts, data.mTopK);
1106+
}
1107+
});
8611108
}
8621109

8631110
////////////////////////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)