Skip to content

Commit bffe3c4

Browse files
committed
v2: drop threadgroup staging + fix grid-imbalance waste; 38%->10% regression
Two-part redesign of the rmsnorm+qmv fusion kernels, root-caused via targeted diagnostics rather than blind iteration: 1. Dropped the threadgroup-staged normed vector entirely (was ~20KB/ threadgroup, capped occupancy to ~1 resident threadgroup/core). x and norm_w are read straight from device memory at the same point the baseline kernel_mul_mv_{q1_0,q2_0}_f32_impl reads y -- they're tiny (~20KB) and stay SLC-resident after the first touch, so this isn't a real bandwidth cost, just matches the baseline's occupancy profile. Also matched NSG/NR0 to upstream's proven N_SG_Q1_0/Q2_0=2 (was an arbitrary 4). Result: regression narrowed 38% -> 23%. 2. Found and fixed a second real bug via live ne01 tracing: the multi-consumer kernel dispatched a UNIFORM (max_tg, N) grid sized to the LARGEST sibling for every N-slot. Real fan-outs are wildly imbalanced (observed ne01=[10240,48,48,6144] on one site) -- the small siblings got >99% wasted threadgroups, each still paying a full norm reduction before exiting on the row-bounds check. Fixed: dispatch once per sibling with a grid sized to THAT sibling's own ne01 (args.which selects it, same pipeline/bind state reused, no PSO rebind cost). Result: regression narrowed 23% -> ~10% (41.24 -> 37.22 t/s). Diagnostic-only experiments (scale hardcoded to 1.0, wrong output, used only to isolate cost -- not shipped) confirm the remaining ~10% gap is the within-matrix redundant reduction: every threadgroup in a sibling's own dispatch still redoes the O(ne00) norm reduction independently (baseline does it exactly once, in a single-threadgroup dispatch). With the reduction removed, throughput recovers to 40.31 -- within ~2% of the 41.24 baseline. Real next step: a separate single-threadgroup norm-scale-only kernel writing one scalar to a small persistent scratch buffer, read (not recomputed) by each matvec dispatch -- not implemented here, needs new scratch-buffer allocation plumbing in ggml-metal that doesn't go through the normal ggml tensor allocator. Bit-exact vs baseline at every step of this redesign, all 126/126 fusion sites still fire. GGML_METAL_RMSNORM_QMV_FUSE stays off by default -- still a measured regression, just a much smaller one.
1 parent 9e85e68 commit bffe3c4

3 files changed

Lines changed: 105 additions & 84 deletions

File tree

ggml/src/ggml-metal/ggml-metal-impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,11 +580,16 @@ typedef struct {
580580
// weight_n [ne00, ne01[n]] -> dst_n [ne01[n]]. Real fan-outs observed on Bonsai-27B
581581
// (Qwen3-Next hybrid): gate+up MLP (N=2), wqkv+wqkv_gate GDN input (N=2),
582582
// q/k/v-family attention input (N=3,4) -- see bonsai-27b-megakernel-repo memory.
583+
// v2: dispatched ONCE per sibling (`which` selects it) with a grid sized to THAT sibling's
584+
// own ne01, not a uniform (max_tg, N) grid -- real fan-outs are wildly imbalanced (observed
585+
// [10240,48,48,6144] on one site), and a uniform grid wasted >99% of threadgroups on the small
586+
// siblings (each still paying a full norm reduction before exiting on the row-bounds check).
583587
typedef struct {
584588
int32_t ne00;
585589
int32_t ne01[4];
586590
uint64_t nb01[4];
587591
float eps;
592+
int32_t which; // which sibling this dispatch serves, 0..n-1
588593
} ggml_metal_kargs_rmsnorm_qmv_multi;
589594

590595
typedef struct {

ggml/src/ggml-metal/ggml-metal-ops.cpp

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3628,11 +3628,12 @@ int ggml_metal_op_rmsnorm_qmv_try(ggml_metal_op_t ctx, int idx) {
36283628
ggml_metal_encoder_set_buffer (enc, bid_normw, 3);
36293629
ggml_metal_encoder_set_buffer (enc, bid_dst, 4);
36303630

3631-
ggml_metal_encoder_set_threadgroup_memory_size(enc, args.ne00 * sizeof(float), 0);
3632-
ggml_metal_encoder_set_threadgroup_memory_size(enc, 32 * sizeof(float), 1);
3631+
// v2: no threadgroup-staged normed vector (see ggml-metal.metal comment) -- only the small
3632+
// reduction scratch remains, at threadgroup(0) now that xn is gone.
3633+
ggml_metal_encoder_set_threadgroup_memory_size(enc, 32 * sizeof(float), 0);
36333634

3634-
const int NSG_HOST = 4; // must match NSG_RMSNORM_QMV in ggml-metal.metal
3635-
const int NR0_HOST = 8; // must match NR0_RMSNORM_QMV in ggml-metal.metal
3635+
const int NSG_HOST = 2; // must match NSG_RMSNORM_QMV in ggml-metal.metal (== N_SG_Q1_0/Q2_0)
3636+
const int NR0_HOST = 8; // must match NR0_RMSNORM_QMV in ggml-metal.metal (== N_R0_Q1_0/Q2_0)
36363637

36373638
const int rows_per_tg = NSG_HOST * NR0_HOST;
36383639
const int num_tg = (args.ne01 + rows_per_tg - 1) / rows_per_tg;
@@ -3735,7 +3736,13 @@ int ggml_metal_op_rmsnorm_qmv_multi_try(ggml_metal_op_t ctx, int idx) {
37353736
}
37363737

37373738
if (trace) {
3738-
GGML_LOG_DEBUG("%s: idx=%d MATCHED n=%d qtype=%s\n", __func__, idx, n, ggml_type_name(qtype));
3739+
char ne01buf[128] = {};
3740+
for (int32_t k = 0; k < n; ++k) {
3741+
char tmp[32];
3742+
snprintf(tmp, sizeof(tmp), "%s%lld", k ? "," : "", (long long) mms[k]->src[0]->ne[1]);
3743+
strncat(ne01buf, tmp, sizeof(ne01buf) - strlen(ne01buf) - 1);
3744+
}
3745+
GGML_LOG_DEBUG("%s: idx=%d MATCHED n=%d qtype=%s ne01=[%s]\n", __func__, idx, n, ggml_type_name(qtype), ne01buf);
37393746
}
37403747

37413748
float eps;
@@ -3781,19 +3788,25 @@ int ggml_metal_op_rmsnorm_qmv_multi_try(ggml_metal_op_t ctx, int idx) {
37813788
ggml_metal_encoder_set_buffer (enc, bid_dst[2], 9);
37823789
ggml_metal_encoder_set_buffer (enc, bid_dst[3], 10);
37833790

3784-
ggml_metal_encoder_set_threadgroup_memory_size(enc, args.ne00 * sizeof(float), 0);
3785-
ggml_metal_encoder_set_threadgroup_memory_size(enc, 32 * sizeof(float), 1);
3791+
// v2: no threadgroup-staged normed vector -- only the small reduction scratch remains.
3792+
ggml_metal_encoder_set_threadgroup_memory_size(enc, 32 * sizeof(float), 0);
37863793

3787-
const int NSG_HOST = 4; // must match NSG_RMSNORM_QMV in ggml-metal.metal
3788-
const int NR0_HOST = 8; // must match NR0_RMSNORM_QMV in ggml-metal.metal
3794+
const int NSG_HOST = 2; // must match NSG_RMSNORM_QMV in ggml-metal.metal (== N_SG_Q1_0/Q2_0)
3795+
const int NR0_HOST = 8; // must match NR0_RMSNORM_QMV in ggml-metal.metal (== N_R0_Q1_0/Q2_0)
37893796
const int rows_per_tg = NSG_HOST * NR0_HOST;
37903797

3791-
int max_tg = 0;
3798+
// Dispatch once per sibling, each with a grid sized to THAT sibling's own ne01 -- a
3799+
// uniform (max_tg, n) grid wastes threadgroups (up to >99% observed) whenever siblings are
3800+
// imbalanced, since every dispatched threadgroup pays a full norm reduction regardless of
3801+
// whether it's in-bounds. Same pipeline/bind state reused across the n calls (cheap --
3802+
// no PSO rebind, just a bytes update + dispatch).
37923803
for (int32_t k = 0; k < n; ++k) {
3793-
max_tg = std::max(max_tg, (args.ne01[k] + rows_per_tg - 1) / rows_per_tg);
3794-
}
3804+
args.which = k;
3805+
ggml_metal_encoder_set_bytes(enc, &args, sizeof(args), 0);
37953806

3796-
ggml_metal_encoder_dispatch_threadgroups(enc, max_tg, n, 1, NSG_HOST * 32, 1, 1);
3807+
const int tg_k = (args.ne01[k] + rows_per_tg - 1) / rows_per_tg;
3808+
ggml_metal_encoder_dispatch_threadgroups(enc, tg_k, 1, 1, NSG_HOST * 32, 1, 1);
3809+
}
37973810

37983811
for (int32_t i = 1; i < 2 + n; ++i) {
37993812
if (!ggml_metal_op_concurrency_check(ctx, ctx->node(idx + i))) {

ggml/src/ggml-metal/ggml-metal.metal

Lines changed: 74 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3776,35 +3776,48 @@ kernel void kernel_mul_mv_q2_0_f32(
37763776
}
37773777

37783778
// Fused RMS_NORM(x)*norm_w -> quantized mat-vec, batch=1 decode only.
3779-
// Replaces two dispatches (kernel_rms_norm_mul_f32 + kernel_mul_mv_{q1_0,q2_0}_f32) with one:
3780-
// the normed vector never leaves threadgroup memory. Reuses this file's own block_q1_0/
3781-
// block_q2_0 dequant/dot-product primitives verbatim (block_q_n_dot_y overloads above) --
3782-
// same NSG/NR0 simdgroup tiling convention as kernel_mul_mv_{q1_0,q2_0}_f32_impl.
3783-
#define NSG_RMSNORM_QMV 4
3779+
// REDESIGNED (v2): v1 staged the full ne00-element normed vector in THREADGROUP memory
3780+
// (~20KB/threadgroup for ne00=5120) to avoid a device round-trip. Measured on real hardware:
3781+
// that regressed tg128 decode ~38% (41.35->25.62 t/s) despite cutting node/barrier counts,
3782+
// because a ~20KB threadgroup memory footprint caps occupancy to ~1 resident threadgroup/core
3783+
// on Apple GPUs -- exactly wrong for a bandwidth-bound batch-1 decode kernel, where occupancy
3784+
// is what hides DRAM latency while streaming the (much larger) quantized weight matrix.
3785+
//
3786+
// The v1 premise was wrong: eliminating "redundant" per-threadgroup reads of x/norm_w was
3787+
// never the real saving -- x and norm_w are tiny (~20KB each) and stay resident in Apple
3788+
// Silicon's SLC after the first touch, so every threadgroup re-reading them from "device"
3789+
// memory is an L2-class cache hit, not DRAM traffic. The actual, real saving from fusion is
3790+
// removing kernel_rms_norm_mul_f32's device-memory WRITE of the normed vector (and the
3791+
// unfused mul_mv's corresponding first read of it) -- one dispatch instead of two, zero
3792+
// extra threadgroup memory. v2: compute the norm SCALE once per threadgroup (tiny reduction,
3793+
// unchanged from v1) but never materialize the normed vector anywhere -- apply
3794+
// x[i]*scale*norm_w[i] inline at the same point kernel_mul_mv_{q1_0,q2_0}_f32_impl reads y[i],
3795+
// reading x/norm_w straight from device memory exactly like that kernel does. Threadgroup
3796+
// memory drops from ~20KB to 128 bytes (the reduction scratch only) -- occupancy should now
3797+
// match the baseline kernel's. NSG/NR0 also switched from an arbitrary 4/8 to match upstream's
3798+
// proven-tuned N_SG_Q1_0/N_SG_Q2_0=2, N_R0_Q1_0/N_R0_Q2_0=8 for this exact quant format.
3799+
#define NSG_RMSNORM_QMV 2
37843800
#define NR0_RMSNORM_QMV 8
37853801

3786-
template<typename block_t, int QK, int nr0>
3787-
void kernel_rmsnorm_qmv_impl(
3788-
constant ggml_metal_kargs_rmsnorm_qmv & args,
3789-
device const char * src0, // quantized weight [ne00, ne01]
3790-
device const float * x, // [ne00]
3791-
device const float * norm_w, // [ne00]
3792-
device float * dst, // [ne01]
3793-
threadgroup float * xn, // ne00 floats
3794-
threadgroup float * red, // >= 32 floats
3802+
// Shared norm-scale reduction: collaboratively computes 1/sqrt(mean(x^2)+eps) across the whole
3803+
// threadgroup. Reads x straight from device memory (cache-resident after first touch, see
3804+
// comment above) -- writes nothing, returns the scalar in every thread.
3805+
inline float rmsnorm_scale(
3806+
device const float * x,
3807+
int32_t ne00,
3808+
float eps,
3809+
threadgroup float * red, // >= 32 floats
37953810
ushort3 tpitg,
37963811
ushort3 ntg,
37973812
ushort tiisg,
3798-
ushort sgitg,
3799-
ushort sgpg, // simdgroups per threadgroup (== NSG_RMSNORM_QMV at dispatch)
3800-
uint tgx) {
3813+
ushort sgitg) {
38013814
if (sgitg == 0) {
38023815
red[tiisg] = 0.0f;
38033816
}
38043817
threadgroup_barrier(mem_flags::mem_threadgroup);
38053818

38063819
float sumsq = 0.0f;
3807-
for (int i = tpitg.x; i < args.ne00; i += ntg.x) {
3820+
for (int i = tpitg.x; i < ne00; i += ntg.x) {
38083821
const float v = x[i];
38093822
sumsq += v * v;
38103823
}
@@ -3818,12 +3831,24 @@ void kernel_rmsnorm_qmv_impl(
38183831
float total = red[tiisg];
38193832
total = simd_sum(total);
38203833

3821-
const float scale = 1.0f / sqrt(total / (float) args.ne00 + args.eps);
3834+
return 1.0f / sqrt(total / (float) ne00 + eps);
3835+
}
38223836

3823-
for (int i = tpitg.x; i < args.ne00; i += ntg.x) {
3824-
xn[i] = x[i] * scale * norm_w[i];
3825-
}
3826-
threadgroup_barrier(mem_flags::mem_threadgroup);
3837+
template<typename block_t, int QK, int nr0>
3838+
void kernel_rmsnorm_qmv_impl(
3839+
constant ggml_metal_kargs_rmsnorm_qmv & args,
3840+
device const char * src0, // quantized weight [ne00, ne01]
3841+
device const float * x, // [ne00]
3842+
device const float * norm_w, // [ne00]
3843+
device float * dst, // [ne01]
3844+
threadgroup float * red, // >= 32 floats
3845+
ushort3 tpitg,
3846+
ushort3 ntg,
3847+
ushort tiisg,
3848+
ushort sgitg,
3849+
ushort sgpg, // simdgroups per threadgroup (== NSG_RMSNORM_QMV at dispatch)
3850+
uint tgx) {
3851+
const float scale = rmsnorm_scale(x, args.ne00, args.eps, red, tpitg, ntg, tiisg, sgitg);
38273852

38283853
const int nb = args.ne00 / QK;
38293854
const int first_row = (int(tgx) * sgpg + sgitg) * nr0;
@@ -3840,18 +3865,20 @@ void kernel_rmsnorm_qmv_impl(
38403865
const short ix = tiisg / 8;
38413866
const short il = (tiisg % 8) * 16;
38423867

3843-
threadgroup const float * yb = xn + ix * QK + il;
3868+
device const float * xb = x + ix * QK + il;
3869+
device const float * wb = norm_w + ix * QK + il;
38443870

38453871
for (int ib = ix; ib < nb; ib += N_SIMDWIDTH / 8) {
38463872
float sumy = 0.0f;
38473873
FOR_UNROLL (short i = 0; i < 16; i++) {
3848-
yl[i] = yb[i];
3849-
sumy += yb[i];
3874+
yl[i] = xb[i] * scale * wb[i];
3875+
sumy += yl[i];
38503876
}
38513877
FOR_UNROLL (short row = 0; row < nr0; row++) {
38523878
sumf[row] += block_q_n_dot_y(ax[row] + ib, sumy, yl, il);
38533879
}
3854-
yb += QK * (N_SIMDWIDTH / 8);
3880+
xb += QK * (N_SIMDWIDTH / 8);
3881+
wb += QK * (N_SIMDWIDTH / 8);
38553882
}
38563883

38573884
for (int row = 0; row < nr0; ++row) {
@@ -3869,15 +3896,14 @@ kernel void kernel_rmsnorm_mv_q1_0_f32(
38693896
device const float * x [[buffer(2)]],
38703897
device const float * norm_w [[buffer(3)]],
38713898
device float * dst [[buffer(4)]],
3872-
threadgroup float * xn [[threadgroup(0)]],
3873-
threadgroup float * red [[threadgroup(1)]],
3899+
threadgroup float * red [[threadgroup(0)]],
38743900
uint3 tgpig [[threadgroup_position_in_grid]],
38753901
ushort3 tpitg [[thread_position_in_threadgroup]],
38763902
ushort3 ntg [[threads_per_threadgroup]],
38773903
ushort tiisg [[thread_index_in_simdgroup]],
38783904
ushort sgitg [[simdgroup_index_in_threadgroup]]) {
38793905
kernel_rmsnorm_qmv_impl<block_q1_0, QK1_0, NR0_RMSNORM_QMV>(
3880-
args, src0, x, norm_w, dst, xn, red, tpitg, ntg, tiisg, sgitg, NSG_RMSNORM_QMV, tgpig.x);
3906+
args, src0, x, norm_w, dst, red, tpitg, ntg, tiisg, sgitg, NSG_RMSNORM_QMV, tgpig.x);
38813907
}
38823908

38833909
[[host_name("kernel_rmsnorm_mv_q2_0_f32")]]
@@ -3887,67 +3913,43 @@ kernel void kernel_rmsnorm_mv_q2_0_f32(
38873913
device const float * x [[buffer(2)]],
38883914
device const float * norm_w [[buffer(3)]],
38893915
device float * dst [[buffer(4)]],
3890-
threadgroup float * xn [[threadgroup(0)]],
3891-
threadgroup float * red [[threadgroup(1)]],
3916+
threadgroup float * red [[threadgroup(0)]],
38923917
uint3 tgpig [[threadgroup_position_in_grid]],
38933918
ushort3 tpitg [[thread_position_in_threadgroup]],
38943919
ushort3 ntg [[threads_per_threadgroup]],
38953920
ushort tiisg [[thread_index_in_simdgroup]],
38963921
ushort sgitg [[simdgroup_index_in_threadgroup]]) {
38973922
kernel_rmsnorm_qmv_impl<block_q2_0, QK2_0, NR0_RMSNORM_QMV>(
3898-
args, src0, x, norm_w, dst, xn, red, tpitg, ntg, tiisg, sgitg, NSG_RMSNORM_QMV, tgpig.x);
3923+
args, src0, x, norm_w, dst, red, tpitg, ntg, tiisg, sgitg, NSG_RMSNORM_QMV, tgpig.x);
38993924
}
39003925

39013926
// Multi-consumer generalization: ONE shared RMSNorm(x)*norm_w feeds N independently-shaped
39023927
// quantized mat-vecs (gate+up MLP N=2, wqkv+wqkv_gate GDN input N=2, attention QKV-family
39033928
// N=3/4 -- exact fan-outs measured live on Bonsai-27B, see bonsai-27b-megakernel-repo memory).
39043929
// Grid: tgpig.y selects which of the N (weight, dst) pairs this threadgroup serves; tgpig.x
3905-
// tiles that matrix's output rows. The norm is recomputed redundantly per y-slot (cheap --
3906-
// O(ne00) vs the O(ne00*ne01) matvec it feeds) in exchange for N-1 fewer kernel dispatches and
3907-
// N-1 fewer device-memory round-trips of the shared normed vector.
3930+
// tiles that matrix's output rows. `args.which` selects the sibling THIS dispatch serves --
3931+
// dispatched once per sibling with a grid sized to that sibling's own ne01 (see host-side
3932+
// comment: a uniform (max_tg, N) grid wasted >99% of threadgroups when siblings are wildly
3933+
// imbalanced, e.g. observed ne01=[10240,48,48,6144] on one real fusion site). Norm SCALE only
3934+
// (no threadgroup-staged vector), x/norm_w read straight from device memory, recomputed
3935+
// redundantly per dispatch (cheap scalar reduction vs the O(ne00*ne01) matvec it feeds).
39083936
template<typename block_t, int QK, int nr0, int N>
39093937
void kernel_rmsnorm_qmv_multi_impl(
39103938
constant ggml_metal_kargs_rmsnorm_qmv_multi & args,
39113939
device const char * srcs[N],
39123940
device const float * x,
39133941
device const float * norm_w,
39143942
device float * dsts[N],
3915-
threadgroup float * xn,
39163943
threadgroup float * red,
39173944
ushort3 tpitg,
39183945
ushort3 ntg,
39193946
ushort tiisg,
39203947
ushort sgitg,
39213948
ushort sgpg,
39223949
uint3 tgpig) {
3923-
if (sgitg == 0) {
3924-
red[tiisg] = 0.0f;
3925-
}
3926-
threadgroup_barrier(mem_flags::mem_threadgroup);
3950+
const float scale = rmsnorm_scale(x, args.ne00, args.eps, red, tpitg, ntg, tiisg, sgitg);
39273951

3928-
float sumsq = 0.0f;
3929-
for (int i = tpitg.x; i < args.ne00; i += ntg.x) {
3930-
const float v = x[i];
3931-
sumsq += v * v;
3932-
}
3933-
sumsq = simd_sum(sumsq);
3934-
3935-
if (tiisg == 0) {
3936-
red[sgitg] = sumsq;
3937-
}
3938-
threadgroup_barrier(mem_flags::mem_threadgroup);
3939-
3940-
float total = red[tiisg];
3941-
total = simd_sum(total);
3942-
3943-
const float scale = 1.0f / sqrt(total / (float) args.ne00 + args.eps);
3944-
3945-
for (int i = tpitg.x; i < args.ne00; i += ntg.x) {
3946-
xn[i] = x[i] * scale * norm_w[i];
3947-
}
3948-
threadgroup_barrier(mem_flags::mem_threadgroup);
3949-
3950-
const uint n = tgpig.y; // which (weight, dst) pair, 0..N-1
3952+
const uint n = (uint) args.which;
39513953
if (n >= (uint) N || args.ne01[n] <= 0) {
39523954
return;
39533955
}
@@ -3970,18 +3972,20 @@ void kernel_rmsnorm_qmv_multi_impl(
39703972
const short ix = tiisg / 8;
39713973
const short il = (tiisg % 8) * 16;
39723974

3973-
threadgroup const float * yb = xn + ix * QK + il;
3975+
device const float * xb = x + ix * QK + il;
3976+
device const float * wb = norm_w + ix * QK + il;
39743977

39753978
for (int ib = ix; ib < nb; ib += N_SIMDWIDTH / 8) {
39763979
float sumy = 0.0f;
39773980
FOR_UNROLL (short i = 0; i < 16; i++) {
3978-
yl[i] = yb[i];
3979-
sumy += yb[i];
3981+
yl[i] = xb[i] * scale * wb[i];
3982+
sumy += yl[i];
39803983
}
39813984
FOR_UNROLL (short row = 0; row < nr0; row++) {
39823985
sumf[row] += block_q_n_dot_y(ax[row] + ib, sumy, yl, il);
39833986
}
3984-
yb += QK * (N_SIMDWIDTH / 8);
3987+
xb += QK * (N_SIMDWIDTH / 8);
3988+
wb += QK * (N_SIMDWIDTH / 8);
39853989
}
39863990

39873991
for (int row = 0; row < nr0; ++row) {
@@ -4006,8 +4010,7 @@ kernel void NAME( \
40064010
device float * dst_1 [[buffer(8)]], \
40074011
device float * dst_2 [[buffer(9)]], \
40084012
device float * dst_3 [[buffer(10)]], \
4009-
threadgroup float * xn [[threadgroup(0)]], \
4010-
threadgroup float * red [[threadgroup(1)]], \
4013+
threadgroup float * red [[threadgroup(0)]], \
40114014
uint3 tgpig [[threadgroup_position_in_grid]], \
40124015
ushort3 tpitg [[thread_position_in_threadgroup]], \
40134016
ushort3 ntg [[threads_per_threadgroup]], \
@@ -4016,7 +4019,7 @@ kernel void NAME( \
40164019
device const char * srcs[4] = { src0_0, src0_1, src0_2, src0_3 }; \
40174020
device float * dsts[4] = { dst_0, dst_1, dst_2, dst_3 }; \
40184021
kernel_rmsnorm_qmv_multi_impl<BLOCK_T, QK, NR0_RMSNORM_QMV, N>( \
4019-
args, srcs, x, norm_w, dsts, xn, red, tpitg, ntg, tiisg, sgitg, NSG_RMSNORM_QMV, tgpig); \
4022+
args, srcs, x, norm_w, dsts, red, tpitg, ntg, tiisg, sgitg, NSG_RMSNORM_QMV, tgpig); \
40204023
}
40214024

40224025
QMV_MULTI_KERNEL(kernel_rmsnorm_mv2_q1_0_f32, block_q1_0, QK1_0, 2)

0 commit comments

Comments
 (0)