@@ -391,7 +391,7 @@ static constexpr __host__ __device__ int calc_rows_per_block(int ncols_dst, int
391391 return 1 ;
392392}
393393
394- template <ggml_type type, int ncols_dst, bool has_fusion, bool small_k = false >
394+ template <ggml_type type, int ncols_dst, bool has_fusion, bool small_k = false , bool residual_only = false >
395395__launch_bounds__ (calc_nwarps(type, ncols_dst, get_device_table_id())*ggml_cuda_get_physical_warp_size(), 1)
396396static __global__ void mul_mat_vec_q(
397397 const void * __restrict__ vx, const void * __restrict__ vy, const int32_t * __restrict__ ids, const ggml_cuda_mm_fusion_args_device fusion, float * __restrict__ dst,
@@ -401,6 +401,8 @@ static __global__ void mul_mat_vec_q(
401401 const uint32_t stride_sample_x, const uint32_t stride_sample_y, const uint32_t stride_sample_dst,
402402 const uint32_t ids_stride) {
403403
404+ static_assert (!residual_only || has_fusion, " residual-only MMVQ requires fusion" );
405+
404406 constexpr int qk = ggml_cuda_type_traits<type>::qk;
405407 constexpr int qi = ggml_cuda_type_traits<type>::qi;
406408 constexpr int vdr = get_vdr_mmvq (type);
@@ -437,7 +439,10 @@ static __global__ void mul_mat_vec_q(
437439 const float * gate_bias = nullptr ;
438440 ggml_glu_op active_glu;
439441
440- if constexpr (has_fusion) {
442+ if constexpr (residual_only) {
443+ use_bias = true ;
444+ x_bias = (const float *) fusion.x_bias ;
445+ } else if constexpr (has_fusion) {
441446 use_gate = fusion.gate != nullptr ;
442447 use_bias = fusion.x_bias != nullptr ;
443448 use_gate_bias = fusion.gate_bias != nullptr && use_gate;
@@ -478,7 +483,7 @@ static __global__ void mul_mat_vec_q(
478483
479484 // partial sum for each thread
480485 float tmp[ncols_dst][rows_per_cuda_block] = {{0 .0f }};
481- float tmp_gate[ncols_dst][rows_per_cuda_block] = {{0 .0f }};
486+ float tmp_gate[ncols_dst][residual_only ? 1 : rows_per_cuda_block] = {{0 .0f }};
482487
483488 const block_q8_1 * y = ((const block_q8_1 *) vy) + sample_y*stride_sample_y + channel_y*stride_channel_y;
484489 const int kbx_offset = sample_x*stride_sample_x + channel_x*stride_channel_x + row0*stride_row_x;
@@ -495,7 +500,7 @@ static __global__ void mul_mat_vec_q(
495500 for (int i = 0 ; i < rows_per_cuda_block; ++i) {
496501 tmp[j][i] += vec_dot_q_cuda (
497502 vx, &y[j*stride_col_y + kby], kbx_offset + i*stride_row_x + kbx, kqs);
498- if constexpr (has_fusion) {
503+ if constexpr (has_fusion && !residual_only ) {
499504 if (use_gate) {
500505 tmp_gate[j][i] += vec_dot_q_cuda (
501506 vgate, &y[j*stride_col_y + kby], kbx_offset + i*stride_row_x + kbx, kqs);
@@ -506,8 +511,8 @@ static __global__ void mul_mat_vec_q(
506511 }
507512
508513 __shared__ float tmp_shared[nwarps-1 > 0 ? nwarps-1 : 1 ][ncols_dst][rows_per_cuda_block][warp_size];
509- __shared__ float tmp_shared_gate[(has_fusion && (nwarps-1 > 0 )) ? nwarps-1 : 1 ][ncols_dst][rows_per_cuda_block][warp_size];
510- if constexpr (!has_fusion) {
514+ __shared__ float tmp_shared_gate[(has_fusion && !residual_only && (nwarps-1 > 0 )) ? nwarps-1 : 1 ][ncols_dst][residual_only ? 1 : rows_per_cuda_block][warp_size];
515+ if constexpr (!has_fusion || residual_only ) {
511516 (void ) tmp_shared_gate;
512517 } else if (!use_gate) {
513518 (void ) tmp_shared_gate;
@@ -519,7 +524,7 @@ static __global__ void mul_mat_vec_q(
519524#pragma unroll
520525 for (int i = 0 ; i < rows_per_cuda_block; ++i) {
521526 tmp_shared[threadIdx .y -1 ][j][i][threadIdx .x ] = tmp[j][i];
522- if constexpr (has_fusion) {
527+ if constexpr (has_fusion && !residual_only ) {
523528 if (use_gate) {
524529 tmp_shared_gate[threadIdx .y -1 ][j][i][threadIdx .x ] = tmp_gate[j][i];
525530 }
@@ -542,14 +547,14 @@ static __global__ void mul_mat_vec_q(
542547#pragma unroll
543548 for (int l = 0 ; l < nwarps-1 ; ++l) {
544549 tmp[j][i] += tmp_shared[l][j][i][threadIdx .x ];
545- if constexpr (has_fusion) {
550+ if constexpr (has_fusion && !residual_only ) {
546551 if (use_gate) {
547552 tmp_gate[j][i] += tmp_shared_gate[l][j][i][threadIdx .x ];
548553 }
549554 }
550555 }
551556 tmp[j][i] = warp_reduce_sum<warp_size>(tmp[j][i]);
552- if constexpr (has_fusion) {
557+ if constexpr (has_fusion && !residual_only ) {
553558 if (use_gate) {
554559 tmp_gate[j][i] = warp_reduce_sum<warp_size>(tmp_gate[j][i]);
555560 }
@@ -562,33 +567,35 @@ static __global__ void mul_mat_vec_q(
562567 if (use_bias) {
563568 result += x_biases[j];
564569 }
565- if (use_gate) {
566- float gate_value = tmp_gate[j][threadIdx .x ];
567- if (use_gate_bias) {
568- gate_value += gate_biases[j];
569- }
570- switch (active_glu) {
571- case GGML_GLU_OP_SWIGLU :
572- result *= ggml_cuda_op_silu_single (gate_value);
573- break ;
574- case GGML_GLU_OP_GEGLU :
575- result *= ggml_cuda_op_gelu_single (gate_value);
576- break ;
577- case GGML_GLU_OP_SWIGLU_OAI : {
578- result = ggml_cuda_op_swiglu_oai_single (gate_value, result);
579- break ;
570+ if constexpr (!residual_only) {
571+ if (use_gate) {
572+ float gate_value = tmp_gate[j][threadIdx .x ];
573+ if (use_gate_bias) {
574+ gate_value += gate_biases[j];
575+ }
576+ switch (active_glu) {
577+ case GGML_GLU_OP_SWIGLU :
578+ result *= ggml_cuda_op_silu_single (gate_value);
579+ break ;
580+ case GGML_GLU_OP_GEGLU :
581+ result *= ggml_cuda_op_gelu_single (gate_value);
582+ break ;
583+ case GGML_GLU_OP_SWIGLU_OAI : {
584+ result = ggml_cuda_op_swiglu_oai_single (gate_value, result);
585+ break ;
586+ }
587+ default :
588+ result = result * gate_value;
589+ break ;
580590 }
581- default :
582- result = result * gate_value;
583- break ;
584591 }
585592 }
586593 }
587594 dst[j*stride_col_dst + threadIdx .x ] = result;
588595 }
589596 }
590597
591- if constexpr (!has_fusion) {
598+ if constexpr (!has_fusion || residual_only ) {
592599 GGML_UNUSED_VARS (use_gate, use_bias, use_gate_bias, active_glu, gate_bias, x_bias, tmp_gate);
593600 }
594601}
@@ -681,6 +688,17 @@ static void mul_mat_vec_q_switch_fusion(
681688 const bool has_fusion = fusion.gate != nullptr || fusion.x_bias != nullptr || fusion.gate_bias != nullptr ;
682689 if constexpr (c_ncols_dst == 1 ) {
683690 if (has_fusion) {
691+ if constexpr (type == GGML_TYPE_Q8_0 ) {
692+ if (fusion.residual_only ) {
693+ mul_mat_vec_q<type, c_ncols_dst, true , small_k, true ><<<block_nums, block_dims, nbytes_shared, stream>>>
694+ (vx, vy, ids, fusion, dst, ncols_x, nchannels_y, stride_row_x, stride_col_y, stride_col_dst,
695+ channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
696+ sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, ids_stride);
697+ return ;
698+ }
699+ }
700+
701+ GGML_ASSERT (!fusion.residual_only && " residual-only MMVQ is supported only for Q8_0" );
684702 mul_mat_vec_q<type, c_ncols_dst, true , small_k><<<block_nums, block_dims, nbytes_shared, stream>>>
685703 (vx, vy, ids, fusion, dst, ncols_x, nchannels_y, stride_row_x, stride_col_y, stride_col_dst,
686704 channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
@@ -1078,6 +1096,7 @@ void ggml_cuda_mul_mat_vec_q(
10781096 fusion_local.gate_bias = fusion->gate_bias ->data ;
10791097 }
10801098 fusion_local.glu_op = fusion->glu_op ;
1099+ fusion_local.residual_only = fusion->residual_only ;
10811100 }
10821101
10831102 // If src0 is a temporary compute buffer, clear any potential padding.
0 commit comments