Skip to content

Commit 852a3bd

Browse files
committed
perf(engine/metal): gelu-fused down-projection kernels (#341 phase 1, capability)
lthn_gelu_qmv + lthn_gather_qmv_gelu fold gemma's MLP gate into the down projection's x-load: each element computes T(gelu_approx(gate)*up) AT LOAD — the exact expression, type flow (bfloat-typed partial sums before widening, per-width x_thread scalings) and single bf16 rounding of the lthn_gelu_gate_mul dispatch it replaces — so the values entering qdot are byte-identical to the chain's gated buffer and the gelu dispatch plus its barrier hop disappear from both MoE chains (local down: qmv shape; routed expert down: the fc-specialised gather with gate/up riding the same lhs indexing). lthn_gelu_qmv_impl.h is a verbatim structural copy of quantized.h's load_vector/load_vector_safe/qmv_impl with only the element reads swapped; both kernels share it, so the two dispatch shapes cannot drift. Instantiated gs {32,64,128} x bits {4,8} (the local/expert down widths). Kernels-only slice: compiled into lthn_kernels.metallib (28 kernels), unreferenced until the host wiring lands with its byte-parity oracle and real-model receipts (#341 task carries the emit map). Co-Authored-By: Virgil <virgil@lethean.io>
1 parent 5d1be81 commit 852a3bd

3 files changed

Lines changed: 531 additions & 0 deletions

File tree

go/engine/metal/kernels/lthn_gather_qmv.metal

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,60 @@ template <typename T, int group_size, int bits>
9393
w, scales, biases, x, y, in_vec_size, out_vec_size, tid, simd_gid, simd_lid);
9494
}
9595

96+
#include "lthn_gelu_qmv_impl.h"
97+
98+
// lthn_gather_qmv_gelu — the routed experts' DOWN gather with the MLP gate
99+
// fused into its x-load (#341 phase 1): each route reads its expert's gate/up
100+
// activation rows and computes gelu(gate)·up at load, byte-identical to the
101+
// chain's gated buffer (lthn_gelu_qmv_impl.h), so the expert gelu dispatch and
102+
// its barrier disappear. Same fc prologue as lthn_gather_qmv; gate and up ride
103+
// the same lhs indexing.
104+
template <typename T, int group_size, int bits>
105+
[[kernel]] void lthn_gather_qmv_gelu(
106+
const device uint32_t* w [[buffer(0)]],
107+
const device T* scales [[buffer(1)]],
108+
const device T* biases [[buffer(2)]],
109+
const device T* gate [[buffer(3)]],
110+
const device T* up [[buffer(4)]],
111+
const device uint32_t* lhs_indices [[buffer(5)]],
112+
const device uint32_t* rhs_indices [[buffer(6)]],
113+
device T* y [[buffer(7)]],
114+
const constant int& in_vec_size [[buffer(8)]],
115+
const constant int& out_vec_size [[buffer(9)]],
116+
uint3 tid [[threadgroup_position_in_grid]],
117+
uint simd_gid [[simdgroup_index_in_threadgroup]],
118+
uint simd_lid [[thread_index_in_simdgroup]]) {
119+
const int64_t w_idx = rhs_indices[tid.z];
120+
w += w_idx * lthn_gather_expert_rows * (int64_t(in_vec_size) * bits / 32);
121+
const int64_t sb_stride = int64_t(lthn_gather_expert_rows) * (in_vec_size / group_size);
122+
scales += w_idx * sb_stride;
123+
biases += w_idx * sb_stride;
124+
if (lthn_gather_batched_x) {
125+
const int64_t xo = int64_t(lhs_indices[tid.z]) * in_vec_size;
126+
gate += xo;
127+
up += xo;
128+
}
129+
y += tid.z * out_vec_size;
130+
qmv_gelu_impl<T, group_size, bits>(
131+
w, scales, biases, gate, up, y, in_vec_size, out_vec_size, tid, simd_gid, simd_lid);
132+
}
133+
134+
#define instantiate_lthn_gather_qmv_gelu(group_size, bits) \
135+
template [[host_name("lthn_gather_qmv_gelu_bfloat16_t_gs_" #group_size \
136+
"_b_" #bits)]] [[kernel]] void \
137+
lthn_gather_qmv_gelu<bfloat16_t, group_size, bits>( \
138+
const device uint32_t*, const device bfloat16_t*, const device bfloat16_t*, \
139+
const device bfloat16_t*, const device bfloat16_t*, const device uint32_t*, \
140+
const device uint32_t*, device bfloat16_t*, \
141+
const constant int&, const constant int&, uint3, uint, uint);
142+
143+
instantiate_lthn_gather_qmv_gelu(32, 4)
144+
instantiate_lthn_gather_qmv_gelu(32, 8)
145+
instantiate_lthn_gather_qmv_gelu(64, 4)
146+
instantiate_lthn_gather_qmv_gelu(64, 8)
147+
instantiate_lthn_gather_qmv_gelu(128, 4)
148+
instantiate_lthn_gather_qmv_gelu(128, 8)
149+
96150
#define instantiate_lthn_gather_qmv(group_size, bits) \
97151
template [[host_name("lthn_gather_qmv_fast_bfloat16_t_gs_" #group_size \
98152
"_b_" #bits)]] [[kernel]] void \
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// SPDX-Licence-Identifier: EUPL-1.2
2+
3+
// lthn_gelu_qmv — the MoE local expert's DOWN projection with the MLP gate
4+
// fused into its x-load (#341 phase 1): y = dequant(W_down) · gelu(gate)·up,
5+
// one dispatch where the chain ran lthn_gelu_gate_mul + affine_qmv with a
6+
// dependency hop between them. Values are byte-identical to the chain (see
7+
// lthn_gelu_qmv_impl.h); the gelu dispatch and its barrier disappear.
8+
//
9+
// Same include chain as MLX's quantized.metal (built with -I external/mlx).
10+
// clang-format off
11+
#include "mlx/backend/metal/kernels/utils.h"
12+
#include "mlx/backend/metal/kernels/steel/gemm/gemm.h"
13+
#include "mlx/backend/metal/kernels/quantized_utils.h"
14+
#include "mlx/backend/metal/kernels/quantized.h"
15+
// clang-format on
16+
17+
#include "lthn_gelu_qmv_impl.h"
18+
19+
template <typename T, int group_size, int bits>
20+
[[kernel]] void lthn_gelu_qmv(
21+
const device uint32_t* w [[buffer(0)]],
22+
const device T* scales [[buffer(1)]],
23+
const device T* biases [[buffer(2)]],
24+
const device T* gate [[buffer(3)]],
25+
const device T* up [[buffer(4)]],
26+
device T* y [[buffer(5)]],
27+
const constant int& in_vec_size [[buffer(6)]],
28+
const constant int& out_vec_size [[buffer(7)]],
29+
uint3 tid [[threadgroup_position_in_grid]],
30+
uint simd_gid [[simdgroup_index_in_threadgroup]],
31+
uint simd_lid [[thread_index_in_simdgroup]]) {
32+
qmv_gelu_impl<T, group_size, bits>(
33+
w, scales, biases, gate, up, y, in_vec_size, out_vec_size, tid, simd_gid, simd_lid);
34+
}
35+
36+
#define instantiate_lthn_gelu_qmv(group_size, bits) \
37+
template [[host_name("lthn_gelu_qmv_bfloat16_t_gs_" #group_size \
38+
"_b_" #bits)]] [[kernel]] void \
39+
lthn_gelu_qmv<bfloat16_t, group_size, bits>( \
40+
const device uint32_t*, const device bfloat16_t*, const device bfloat16_t*, \
41+
const device bfloat16_t*, const device bfloat16_t*, device bfloat16_t*, \
42+
const constant int&, const constant int&, uint3, uint, uint);
43+
44+
instantiate_lthn_gelu_qmv(32, 4)
45+
instantiate_lthn_gelu_qmv(32, 8)
46+
instantiate_lthn_gelu_qmv(64, 4)
47+
instantiate_lthn_gelu_qmv(64, 8)
48+
instantiate_lthn_gelu_qmv(128, 4)
49+
instantiate_lthn_gelu_qmv(128, 8)

0 commit comments

Comments
 (0)