@@ -789,6 +789,76 @@ static __global__ void mul_mat_vec_q(
789789 }
790790#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
791791
792+ // PTQ1_0 MoE (ids), one column, K small enough for one block per lane: the generic loop below puts one
793+ // 128-weight block on each thread of the whole CTA, so with K <= 4096 only warp 0 loads while warps 1..3 idle.
794+ // Warp-per-row instead: warp w owns row row0+w and its lanes read that row's blocks (planar PT layout).
795+ // With at most 32 blocks per row every lane holds one block and the lane sums then go through the same
796+ // warp_reduce_sum butterfly as the generic path, so the result is unchanged.
797+ #if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
798+ if constexpr (small_k && type == GGML_TYPE_PTQ1_0 && ncols_dst == 1 && !y_soa && nwarps > 1 && rows_per_cuda_block == nwarps) {
799+ if (ids != nullptr && blocks_per_row_x <= warp_size) {
800+ const int warp = threadIdx .y ;
801+ const int lane = threadIdx .x ;
802+ const bool row_ok = uint32_t (row0 + warp) < nrows_dst;
803+
804+ float acc = 0 .0f ;
805+ float acc_gate = 0 .0f ;
806+ if (row_ok && lane < blocks_per_row_x) {
807+ const int nblk = ptq1_0_pt_nblk (ncols_x);
808+ const char * ycol[1 ] = { (const char *) y };
809+ const block_ptq1_0 * bq[1 ] = { (const block_ptq1_0 *) vx + kbx_offset + warp*stride_row_x + lane };
810+ float dots[1 ][1 ];
811+ ptq1_0_pt_block_dot<1 , 1 >(bq, ycol, lane, nblk, dots);
812+ acc += dots[0 ][0 ];
813+ if constexpr (has_fusion && has_gate) {
814+ const block_ptq1_0 * bg[1 ] = { (const block_ptq1_0 *) vgate + kbx_offset + warp*stride_row_x + lane };
815+ ptq1_0_pt_block_dot<1 , 1 >(bg, ycol, lane, nblk, dots);
816+ acc_gate += dots[0 ][0 ];
817+ }
818+ }
819+ acc = warp_reduce_sum<warp_size>(acc);
820+ if constexpr (has_fusion && has_gate) {
821+ acc_gate = warp_reduce_sum<warp_size>(acc_gate);
822+ }
823+ if (lane == 0 && row_ok) {
824+ float * dst_row = dst + sample_dst*stride_sample_dst + channel_dst*stride_channel_dst + row0 + warp;
825+ [[maybe_unused]] const uint32_t channel_bias = ids ? channel_x : channel_dst;
826+ [[maybe_unused]] const int64_t bias_off = sample_dst*stride_sample_dst + channel_bias*stride_channel_dst + row0 + warp;
827+ float result = acc;
828+ if constexpr (has_fusion) {
829+ if (use_bias) {
830+ result += ((const float *) fusion.x_bias )[bias_off];
831+ }
832+ if constexpr (has_gate) {
833+ float gate_value = acc_gate;
834+ if (use_gate_bias) {
835+ gate_value += ((const float *) fusion.gate_bias )[bias_off];
836+ }
837+ switch (active_glu) {
838+ case GGML_GLU_OP_SWIGLU :
839+ result *= ggml_cuda_op_silu_single (gate_value);
840+ break ;
841+ case GGML_GLU_OP_GEGLU :
842+ result *= ggml_cuda_op_gelu_single (gate_value);
843+ break ;
844+ case GGML_GLU_OP_SWIGLU_OAI :
845+ result = ggml_cuda_op_swiglu_oai_single (gate_value, result);
846+ break ;
847+ default :
848+ result = result * gate_value;
849+ break ;
850+ }
851+ }
852+ }
853+ dst_row[0 ] = result;
854+ }
855+ GGML_UNUSED_VARS (use_gate, use_scale, use_gate_scale, gate_bias, x_bias, x_scale, gate_scale, x_scales,
856+ gate_scales, x_biases, gate_biases, tmp, tmp_gate, vec_dot_q_cuda, blocks_per_iter);
857+ return ;
858+ }
859+ }
860+ #endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
861+
792862 if constexpr ((type == GGML_TYPE_Q1_0 || type == GGML_TYPE_Q2_0 || type == GGML_TYPE_PQ2_0 ) &&
793863 table_id == MMVQ_PARAMETERS_GB10 ) {
794864 using block_t = std::conditional_t <type == GGML_TYPE_Q1_0 , block_q1_0,
0 commit comments