@@ -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+
777923void 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+
7911016template <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
8561082static 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