@@ -67,15 +67,18 @@ enum mmvq_parameter_table_id {
6767 MMVQ_PARAMETERS_GCN ,
6868 MMVQ_PARAMETERS_RDNA2 ,
6969 MMVQ_PARAMETERS_RDNA3_0 ,
70+ MMVQ_PARAMETERS_RDNA3_5 ,
7071 MMVQ_PARAMETERS_RDNA4
7172};
7273
7374static constexpr __device__ mmvq_parameter_table_id get_device_table_id () {
7475#if defined(RDNA4)
7576 return MMVQ_PARAMETERS_RDNA4 ;
76- #elif defined(RDNA3_0)
77+ #elif defined(RDNA3_5)
78+ return MMVQ_PARAMETERS_RDNA3_5 ;
79+ #elif defined(RDNA3)
7780 return MMVQ_PARAMETERS_RDNA3_0 ;
78- #elif defined(RDNA2) || defined(RDNA3_5)
81+ #elif defined(RDNA2)
7982 return MMVQ_PARAMETERS_RDNA2 ;
8083#elif defined(GCN) || defined(CDNA)
8184 return MMVQ_PARAMETERS_GCN ;
@@ -90,10 +93,13 @@ static __host__ mmvq_parameter_table_id get_device_table_id(int cc) {
9093 if (GGML_CUDA_CC_IS_RDNA4 (cc)) {
9194 return MMVQ_PARAMETERS_RDNA4 ;
9295 }
96+ if (GGML_CUDA_CC_IS_RDNA3_5 (cc)) {
97+ return MMVQ_PARAMETERS_RDNA3_5 ;
98+ }
9399 if (GGML_CUDA_CC_IS_RDNA3_0 (cc)) {
94100 return MMVQ_PARAMETERS_RDNA3_0 ;
95101 }
96- if (GGML_CUDA_CC_IS_RDNA2 (cc) || GGML_CUDA_CC_IS_RDNA3_5 (cc) ) {
102+ if (GGML_CUDA_CC_IS_RDNA2 (cc)) {
97103 return MMVQ_PARAMETERS_RDNA2 ;
98104 }
99105 if (GGML_CUDA_CC_IS_GCN (cc) || GGML_CUDA_CC_IS_CDNA (cc)) {
@@ -422,6 +428,9 @@ static constexpr __host__ __device__ int calc_nwarps(ggml_type type, int ncols_d
422428 }
423429 return 1 ;
424430 }
431+ if (table_id == MMVQ_PARAMETERS_RDNA3_5 ) {
432+ return 1 ;
433+ }
425434 if (table_id == MMVQ_PARAMETERS_TURING ) {
426435 if (ncols_dst == 1 ) {
427436 switch (type) {
@@ -674,6 +683,86 @@ static __global__ void mul_mat_vec_q(
674683 }
675684}
676685
686+ // Zero scattered output locations for split-K.
687+ // Grid: (nrows, nchannels_dst, nsamples) Block: (1)
688+ static __global__ void mmvq_splitk_zero_output (
689+ float * dst, const uint32_t stride_channel_dst, const uint32_t stride_sample_dst) {
690+ dst[blockIdx .z *stride_sample_dst + blockIdx .y *stride_channel_dst + blockIdx .x ] = 0 .0f ;
691+ }
692+
693+ // Add bias to split-K output.
694+ // Grid: (ceil(nrows/256), nchannels_dst, nsamples) Block: (256)
695+ static __global__ void mmvq_splitk_add_bias (
696+ float * dst, const float * bias, const uint32_t nrows,
697+ const uint32_t stride_channel_dst, const uint32_t stride_sample_dst) {
698+ const uint32_t row = blockIdx .x * blockDim .x + threadIdx .x ;
699+ if (row >= nrows) {
700+ return ;
701+ }
702+ const uint32_t offset = blockIdx .z *stride_sample_dst + blockIdx .y *stride_channel_dst + row;
703+ dst[offset] += bias[offset];
704+ }
705+
706+ // Split-K MMVQ kernel for RDNA 3.5: splits each row's K-dimension across split_k_factor blocks.
707+ // Each block computes a partial dot product and atomicAdds to the output.
708+ // Grid: (nrows, nchannels_dst, split_k_factor * nsamples)
709+ // Block: (warp_size, 1, 1) — single wave, no shared memory reduction.
710+ template <ggml_type type, int split_k_factor>
711+ __launch_bounds__ (ggml_cuda_get_physical_warp_size(), 1)
712+ static __global__ void mul_mat_vec_q_splitk(
713+ const void * vx_ptr, const void * vy_ptr, float * dst_ptr,
714+ const uint32_t ncols_x, const uint3 nchannels_y,
715+ const uint32_t stride_row_x, const uint32_t stride_col_y, const uint32_t stride_col_dst,
716+ const uint3 channel_ratio, const uint32_t stride_channel_x,
717+ const uint32_t stride_channel_y, const uint32_t stride_channel_dst,
718+ const uint3 sample_ratio, const uint32_t stride_sample_x,
719+ const uint32_t stride_sample_y, const uint32_t stride_sample_dst) {
720+ const void * GGML_CUDA_RESTRICT vx = vx_ptr;
721+ const void * GGML_CUDA_RESTRICT vy = vy_ptr;
722+ float * GGML_CUDA_RESTRICT dst = dst_ptr;
723+
724+ constexpr int qk = ggml_cuda_type_traits<type>::qk;
725+ constexpr int qi = ggml_cuda_type_traits<type>::qi;
726+ constexpr int vdr = get_vdr_mmvq (type);
727+ constexpr int warp_size = ggml_cuda_get_physical_warp_size ();
728+
729+ constexpr vec_dot_q_cuda_t vec_dot_q_cuda = get_vec_dot_q_cuda (type);
730+
731+ const int row0 = blockIdx .x ;
732+ const int blocks_per_row_x = ncols_x / qk;
733+ constexpr int blocks_per_iter = vdr * warp_size / qi;
734+
735+ const uint32_t channel_dst = blockIdx .y ;
736+ const uint32_t split_chunk = blockIdx .z % split_k_factor;
737+ const uint32_t sample_dst = blockIdx .z / split_k_factor;
738+
739+ const uint32_t channel_x = fastdiv (channel_dst, channel_ratio);
740+ const uint32_t channel_y = channel_dst;
741+ const uint32_t sample_x = fastdiv (sample_dst, sample_ratio);
742+ const uint32_t sample_y = sample_dst;
743+
744+ const int blocks_per_chunk = (blocks_per_row_x + split_k_factor - 1 ) / split_k_factor;
745+ const int k_start = split_chunk * blocks_per_chunk;
746+ const int k_end = min (k_start + blocks_per_chunk, blocks_per_row_x);
747+
748+ const block_q8_1 * y = ((const block_q8_1 *) vy) + sample_y*stride_sample_y + channel_y*stride_channel_y;
749+ const int kbx_offset = sample_x*stride_sample_x + channel_x*stride_channel_x + row0*stride_row_x;
750+
751+ float tmp = 0 .0f ;
752+
753+ for (int kbx = k_start + threadIdx .x / (qi/vdr); kbx < k_end; kbx += blocks_per_iter) {
754+ const int kby = kbx * (qk/QK8_1 );
755+ const int kqs = vdr * (threadIdx .x % (qi/vdr));
756+ tmp += vec_dot_q_cuda (vx, &y[kby], kbx_offset + kbx, kqs);
757+ }
758+
759+ tmp = warp_reduce_sum<warp_size>(tmp);
760+
761+ if (threadIdx .x == 0 ) {
762+ atomicAdd (&dst[sample_dst*stride_sample_dst + channel_dst*stride_channel_dst + row0], tmp);
763+ }
764+ }
765+
677766// Dedicated MoE multi-token kernel.
678767// Grid: (ceil(nrows_x / c_rows_per_block), nchannels_dst)
679768// Block: (warp_size, ncols_dst) - each warp handles one token independently.
@@ -890,6 +979,57 @@ static void mul_mat_vec_q_switch_ncols_dst(
890979 case 1 : {
891980 constexpr int c_ncols_dst = 1 ;
892981
982+ // Bias-only defusion for RDNA 3.5: run non-fused kernel + separate bias-add.
983+ // The fused template generates slower code for bias-only cases on RDNA 3.5.
984+ if (table_id == MMVQ_PARAMETERS_RDNA3_5 && has_fusion && fusion.gate == nullptr && fusion.x_bias != nullptr && !has_ids) {
985+ const ggml_cuda_mm_fusion_args_device no_fusion{};
986+ std::pair<dim3 , dim3 > dims = calc_launch_params<type>(c_ncols_dst, nrows_x, nchannels_dst,
987+ nsamples_dst, warp_size, table_id);
988+ mul_mat_vec_q_switch_fusion<type, c_ncols_dst>(
989+ vx, vy, ids, no_fusion, dst, ncols_x, nchannels_y_fd, stride_row_x, stride_col_y, stride_col_dst,
990+ channel_ratio_fd, stride_channel_x, stride_channel_y, stride_channel_dst, sample_ratio_fd,
991+ stride_sample_x, stride_sample_y, stride_sample_dst, dims.first , dims.second , 0 , ids_stride,
992+ stream);
993+
994+ const int bias_threads = 256 ;
995+ const dim3 bias_grid ((nrows_x + bias_threads - 1 ) / bias_threads, nchannels_dst, nsamples_dst);
996+ mmvq_splitk_add_bias<<<bias_grid, bias_threads, 0 , stream>>> (
997+ dst, (const float *) fusion.x_bias , nrows_x, stride_channel_dst, stride_sample_dst);
998+ CUDA_CHECK (cudaGetLastError ());
999+ break ;
1000+ }
1001+
1002+ // Non-fused split-K for RDNA 3.5: increase wave count to hide LPDDR5X latency
1003+ if (table_id == MMVQ_PARAMETERS_RDNA3_5 && !has_fusion && !has_ids) {
1004+ constexpr int qk = ggml_cuda_type_traits<type>::qk;
1005+ const int blocks_per_row = ncols_x / qk;
1006+ const int waves_per_cu = nrows_x / 40 ;
1007+ if (waves_per_cu < 80 && blocks_per_row >= 4 ) {
1008+ const int split_k = (waves_per_cu < 40 ) ? 4 : 2 ;
1009+
1010+ const dim3 zero_grid (nrows_x, nchannels_dst, nsamples_dst);
1011+ mmvq_splitk_zero_output<<<zero_grid, 1 , 0 , stream>>> (dst, stride_channel_dst, stride_sample_dst);
1012+ CUDA_CHECK (cudaGetLastError ());
1013+
1014+ const dim3 block_nums (nrows_x, nchannels_dst, split_k * nsamples_dst);
1015+ const dim3 block_dims (warp_size, 1 , 1 );
1016+ const ggml_cuda_kernel_launch_params launch_params (block_nums, block_dims, 0 , stream);
1017+
1018+ if (split_k == 4 ) {
1019+ ggml_cuda_kernel_launch (mul_mat_vec_q_splitk<type, 4 >, launch_params,
1020+ vx, vy, dst, ncols_x, nchannels_y_fd, stride_row_x, stride_col_y, stride_col_dst,
1021+ channel_ratio_fd, stride_channel_x, stride_channel_y, stride_channel_dst,
1022+ sample_ratio_fd, stride_sample_x, stride_sample_y, stride_sample_dst);
1023+ } else {
1024+ ggml_cuda_kernel_launch (mul_mat_vec_q_splitk<type, 2 >, launch_params,
1025+ vx, vy, dst, ncols_x, nchannels_y_fd, stride_row_x, stride_col_y, stride_col_dst,
1026+ channel_ratio_fd, stride_channel_x, stride_channel_y, stride_channel_dst,
1027+ sample_ratio_fd, stride_sample_x, stride_sample_y, stride_sample_dst);
1028+ }
1029+ break ;
1030+ }
1031+ }
1032+
8931033 bool use_small_k = should_use_small_k (c_ncols_dst);
8941034
8951035 if (use_small_k) {
@@ -1183,12 +1323,34 @@ void ggml_cuda_mul_mat_vec_q(
11831323 }
11841324
11851325 const int64_t ne10_padded = GGML_PAD (ne10, MATRIX_ROW_PADDING );
1186- ggml_cuda_pool_alloc<char > src1_q8_1 (ctx.pool (), ne13*ne12 * ne11*ne10_padded * sizeof (block_q8_1)/QK8_1 );
1187- {
1188- const int64_t s11 = src1->nb [1 ] / ts_src1;
1189- const int64_t s12 = src1->nb [2 ] / ts_src1;
1190- const int64_t s13 = src1->nb [3 ] / ts_src1;
1191- quantize_row_q8_1_cuda (src1_d, nullptr , src1_q8_1.get (), src0->type , ne10, s11, s12, s13, ne10_padded, ne11, ne12, ne13, stream);
1326+ const size_t q8_1_size = ne13*ne12 * ne11*ne10_padded * sizeof (block_q8_1)/QK8_1 ;
1327+
1328+ bool q8_cache_hit = false ;
1329+ void * q8_1_ptr = ctx.q8_1_cache_get_or_alloc (src1->data , q8_1_size, &q8_cache_hit);
1330+
1331+ ggml_cuda_pool_alloc<char > src1_q8_1_pool;
1332+ if (!q8_1_ptr) {
1333+ src1_q8_1_pool.alloc (ctx.pool (), q8_1_size);
1334+ q8_1_ptr = src1_q8_1_pool.get ();
1335+ }
1336+
1337+ if (!q8_cache_hit) {
1338+ if (fusion && fusion->rms_norm_src ) {
1339+ const ggml_tensor * rms_src = fusion->rms_norm_src ;
1340+ const size_t ts_rms = ggml_type_size (rms_src->type );
1341+ const int64_t rs01 = rms_src->nb [1 ] / ts_rms;
1342+ const int64_t rs02 = rms_src->nb [2 ] / ts_rms;
1343+ const int64_t rs03 = rms_src->nb [3 ] / ts_rms;
1344+ quantize_row_q8_1_rms_norm_cuda (
1345+ (const float *)rms_src->data , (const float *)fusion->rms_norm_weights ->data ,
1346+ q8_1_ptr, fusion->rms_norm_eps ,
1347+ ne10, rs01, rs02, rs03, ne10_padded, ne11, ne12, ne13, stream);
1348+ } else {
1349+ const int64_t s11 = src1->nb [1 ] / ts_src1;
1350+ const int64_t s12 = src1->nb [2 ] / ts_src1;
1351+ const int64_t s13 = src1->nb [3 ] / ts_src1;
1352+ quantize_row_q8_1_cuda (src1_d, nullptr , q8_1_ptr, src0->type , ne10, s11, s12, s13, ne10_padded, ne11, ne12, ne13, stream);
1353+ }
11921354 }
11931355
11941356 const int64_t s01 = src0->nb [1 ] / ts_src0;
@@ -1214,7 +1376,7 @@ void ggml_cuda_mul_mat_vec_q(
12141376 const int64_t ids_stride = ids ? ids->nb [1 ] / ggml_type_size (ids->type ) : 0 ;
12151377
12161378 mul_mat_vec_q_switch_type (
1217- src0->data , src0->type , src1_q8_1. get () , ids_d, fusion_local, dst_d, ne00,
1379+ src0->data , src0->type , q8_1_ptr , ids_d, fusion_local, dst_d, ne00,
12181380 ne01, ncols_dst, s01, stride_col_y, stride_col_dst,
12191381 ne02, nchannels_y, nchannels_dst, s02, stride_channel_y, stride_channel_dst,
12201382 ne03, ne3, s03, s13, s3, ids_stride, stream);
0 commit comments