@@ -525,6 +525,125 @@ METAL_FUNC void fp_qmv_impl(
525525 }
526526}
527527
528+ // Quantized matrix-vector for a small batch of input vectors, the M in
529+ // [2, vector_limit) band between qmv (M==1) and qmm. Each thread owns one
530+ // output row that k_lanes lanes reduce over K; the vecs_per_tg vectors are
531+ // streamed so each weight group is dequantized once and reused across them.
532+ template <typename T, int group_size, int bits, int vecs_per_tg, int k_lanes>
533+ METAL_FUNC void fp_qmv_wide_impl (
534+ const device uint32_t * w,
535+ const device uint8_t * scales,
536+ const device T* x,
537+ device T* y,
538+ const constant int & in_vec_size,
539+ const constant int & out_vec_size,
540+ const constant int & M,
541+ uint3 tid [[threadgroup_position_in_grid]] ,
542+ uint simd_gid [[simdgroup_index_in_threadgroup]] ,
543+ uint simd_lid [[thread_index_in_simdgroup]] ) {
544+ constexpr int num_simdgroups = 2 ;
545+ constexpr int results_per_simdgroup = SIMD_SIZE / k_lanes;
546+ constexpr int pack_factor = get_pack_factor<32 , bits>();
547+ constexpr int bytes_per_pack = get_bytes_per_pack<32 >();
548+ constexpr int nf4 = group_size / 4 ; // float4 lanes per quant group
549+
550+ typedef float U;
551+
552+ const short k_lane =
553+ simd_lid % k_lanes; // this lane's slot in the K reduction
554+ const short sg_row = simd_lid / k_lanes; // which output row of the simdgroup
555+
556+ const int out_row = tid.y * (results_per_simdgroup * num_simdgroups) +
557+ results_per_simdgroup * simd_gid + sg_row;
558+ const int vec0 = tid.x * vecs_per_tg; // first input vector handled here
559+
560+ const int row = min (out_row, out_vec_size - 1 );
561+
562+ const int in_vec_size_w = in_vec_size * bytes_per_pack / pack_factor;
563+ const int in_vec_size_g = in_vec_size / group_size;
564+ const device uint8_t * wrow = (const device uint8_t *)w + row * in_vec_size_w;
565+ const device uint8_t * srow = scales + row * in_vec_size_g;
566+
567+ // One device pointer per streamed vector; the clamp keeps an out-of-range
568+ // tail slot reading a valid row (it is never written below).
569+ const device T* xv[vecs_per_tg];
570+ for (int v = 0 ; v < vecs_per_tg; v++) {
571+ xv[v] = x + min (vec0 + v, M - 1 ) * in_vec_size;
572+ }
573+
574+ U result[vecs_per_tg] = {0 };
575+
576+ // Each lane reduces a strided subset of the row's quant groups: one group is
577+ // dequantized to float4 lanes and dot()'d against each streamed vector. One
578+ // group per iteration keeps weight-register pressure low for occupancy.
579+ for (int g = k_lane; g < in_vec_size_g; g += k_lanes) {
580+ const int k0 = g * group_size;
581+ U s = dequantize_scale<U, group_size>(srow[g]);
582+ const device uint8_t * wg = wrow + k0 * bytes_per_pack / pack_factor;
583+
584+ float4 w4[nf4];
585+ if constexpr (bits == 4 ) {
586+ const device uint16_t * wq = (const device uint16_t *)wg;
587+ #pragma unroll
588+ for (int i = 0 ; i < nf4; i++) {
589+ w4[i] = float4 (
590+ Dequantize<4 >{}(wq[i]),
591+ Dequantize<4 >{}(wq[i] >> 4 ),
592+ Dequantize<4 >{}(wq[i] >> 8 ),
593+ Dequantize<4 >{}(wq[i] >> 12 ));
594+ }
595+ } else {
596+ #pragma unroll
597+ for (int i = 0 ; i < nf4; i++) {
598+ w4[i] = float4 (
599+ Dequantize<8 >{}(wg[4 * i]),
600+ Dequantize<8 >{}(wg[4 * i + 1 ]),
601+ Dequantize<8 >{}(wg[4 * i + 2 ]),
602+ Dequantize<8 >{}(wg[4 * i + 3 ]));
603+ }
604+ }
605+
606+ #pragma unroll
607+ for (int v = 0 ; v < vecs_per_tg; v++) {
608+ const device vec<T, 4 >* xv4 = (const device vec<T, 4 >*)(xv[v] + k0);
609+ float acc = 0 ;
610+ #pragma unroll
611+ for (int j = 0 ; j < nf4; j++) {
612+ acc += dot (w4[j], float4 (xv4[j]));
613+ }
614+ result[v] += s * acc;
615+ }
616+ }
617+
618+ // Reduce each vector's partial over its k_lanes with a shuffle ladder:
619+ // simd_sum would mix the results_per_simdgroup rows a simdgroup spans.
620+ for (int v = 0 ; v < vecs_per_tg; v++) {
621+ if constexpr (k_lanes >= 32 ) {
622+ result[v] += simd_shuffle_down (result[v], 16 );
623+ }
624+ if constexpr (k_lanes >= 16 ) {
625+ result[v] += simd_shuffle_down (result[v], 8 );
626+ }
627+ if constexpr (k_lanes >= 8 ) {
628+ result[v] += simd_shuffle_down (result[v], 4 );
629+ }
630+ if constexpr (k_lanes >= 4 ) {
631+ result[v] += simd_shuffle_down (result[v], 2 );
632+ }
633+ if constexpr (k_lanes >= 2 ) {
634+ result[v] += simd_shuffle_down (result[v], 1 );
635+ }
636+ }
637+
638+ if (k_lane == 0 && out_row < out_vec_size) {
639+ for (int v = 0 ; v < vecs_per_tg; v++) {
640+ if (vec0 + v < M) {
641+ y[(vec0 + v) * out_vec_size + out_row] = static_cast <T>(result[v]);
642+ }
643+ }
644+ }
645+ }
646+
528647template <typename T, const int group_size, int bits>
529648METAL_FUNC void fp_qvm_impl (
530649 const device uint32_t * w,
@@ -1087,6 +1206,51 @@ template <typename T, const int group_size, int bits, bool batched>
10871206 w, scales, x, y, in_vec_size, out_vec_size, tid, simd_gid, simd_lid);
10881207}
10891208
1209+ template <
1210+ typename T,
1211+ int group_size,
1212+ int bits,
1213+ int vecs_per_tg,
1214+ int k_lanes,
1215+ bool batched>
1216+ [[kernel]] void fp_qmv_wide (
1217+ const device uint32_t * w,
1218+ const device uint8_t * scales,
1219+ const device T* x,
1220+ device T* y,
1221+ const constant int & in_vec_size,
1222+ const constant int & out_vec_size,
1223+ const constant int & M,
1224+ const constant int & x_batch_ndims,
1225+ const constant int * x_shape,
1226+ const constant int64_t * x_strides,
1227+ const constant int & w_batch_ndims,
1228+ const constant int * w_shape,
1229+ const constant int64_t * w_strides,
1230+ const constant int64_t * s_strides,
1231+ uint3 tid [[threadgroup_position_in_grid]] ,
1232+ uint simd_gid [[simdgroup_index_in_threadgroup]] ,
1233+ uint simd_lid [[thread_index_in_simdgroup]] ) {
1234+ if (batched) {
1235+ adjust_matrix_offsets (
1236+ x,
1237+ w,
1238+ scales,
1239+ y,
1240+ out_vec_size * M,
1241+ x_batch_ndims,
1242+ x_shape,
1243+ x_strides,
1244+ w_batch_ndims,
1245+ w_shape,
1246+ w_strides,
1247+ s_strides,
1248+ tid);
1249+ }
1250+ fp_qmv_wide_impl<T, group_size, bits, vecs_per_tg, k_lanes>(
1251+ w, scales, x, y, in_vec_size, out_vec_size, M, tid, simd_gid, simd_lid);
1252+ }
1253+
10901254template <typename T, const int group_size, int bits, bool batched>
10911255[[kernel]] void fp_qvm (
10921256 const device uint32_t * w,
0 commit comments