devin-lai's picture
Release v0.4.0: selective MLX model downloads
3da88e3 verified
Raw History Blame Contribute Delete
5.7 kB
"""Research kernels. No default dispatch until full-output validation."""
from functools import lru_cache
import mlx.core as mx
from experiments.v2_mpp import gemm
class TunedFC2:
def __init__(self, layer):
self.original = layer
self.bias = layer.bias
self.weight = layer.weight
self.zero = mx.zeros(self.weight.shape[0], self.weight.dtype)
@property
def nbytes(self):
return self.original.nbytes
def __call__(self, x):
if x.ndim != 2 or x.dtype != mx.bfloat16 or self.weight.shape != (5376, 14336):
return self.original(x)
return gemm(
x,
self.weight,
self.zero if self.bias is None else self.bias,
64,
128,
512,
4,
8,
)
@lru_cache(None)
def _qk():
return mx.fast.metal_kernel(
name="h3_r3_qk_native_reduce",
input_names=["qkv", "qw", "kw", "cosine", "sine", "epsilon"],
output_names=["qout", "kout"],
source=r"""
uint lane = thread_position_in_threadgroup.x;
uint row = threadgroup_position_in_grid.y;
uint heads = qkv_shape[2], dim = qkv_shape[3];
uint token = row / heads, head = row % heads;
uint qb = token * 3 * heads * dim + head * dim;
uint kb = qb + heads * dim;
uint rot = cosine_shape[1];
float qsum = 0, ksum = 0;
#pragma unroll
for (uint j = 0; j < 4; ++j) {
uint c = lane * 4 + j;
float q = float(qkv[qb+c]), k = float(qkv[kb+c]);
qsum += q*q; ksum += k*k;
}
float qi = metal::precise::rsqrt(simd_sum(qsum)/float(dim)+epsilon[0]);
float ki = metal::precise::rsqrt(simd_sum(ksum)/float(dim)+epsilon[0]);
#pragma unroll
for (uint j = 0; j < 4; ++j) {
uint c = lane * 4 + j;
T qn = T(float(qw[c])*(float(qkv[qb+c])*qi));
T kn = T(float(kw[c])*(float(qkv[kb+c])*ki));
if (c < rot) {
uint partner = (c+rot/2)%rot;
T qother = T(float(qw[partner])*(float(qkv[qb+partner])*qi));
T kother = T(float(kw[partner])*(float(qkv[kb+partner])*ki));
if (c < rot/2) { qother = -qother; kother = -kother; }
T qcos = T(qn*cosine[token*rot+c]);
T kcos = T(kn*cosine[token*rot+c]);
T qsin = T(qother*sine[token*rot+c]);
T ksin = T(kother*sine[token*rot+c]);
qn = T(qcos+qsin); kn = T(kcos+ksin);
}
uint offset = HEAD_MAJOR ? (head*qkv_shape[0]+token)*dim+c : row*dim+c;
qout[offset] = qn; kout[offset] = kn;
}
""",
)
def qk_native_reduce(qkv, qw, kw, cos, sin, eps=1e-5, *, head_major=False):
n, _, heads, dim = qkv.shape
if dim != 128:
raise ValueError("Research kernel only supports D128")
shape = (1, heads, n, dim) if head_major else (1, n, heads, dim)
return _qk()(
inputs=[
qkv,
qw,
kw,
cos.reshape(n, -1),
sin.reshape(n, -1),
mx.array([eps], mx.float32),
],
template=[("T", qkv.dtype), ("HEAD_MAJOR", head_major)],
grid=(32, n * heads, 1),
threadgroup=(32, 1, 1),
output_shapes=[shape, shape],
output_dtypes=[qkv.dtype, qkv.dtype],
)
@lru_cache(None)
def _norm():
return mx.fast.metal_kernel(
name="h3_r3_norm_mod_native_reduce",
input_names=["x", "weight", "shift", "scale", "indices", "epsilon"],
output_names=["out"],
source=r"""
uint tid = thread_position_in_threadgroup.x;
uint row = threadgroup_position_in_grid.y;
uint width = x_shape[1];
float sum = 0;
for (uint r = 0; r < width; r += TG*4) {
#pragma unroll
for (uint j = 0; j < 4; ++j) {
uint c = r + tid*4+j;
if (c < width) { float a = float(x[row*width+c]); sum += a*a; }
}
}
sum = simd_sum(sum);
threadgroup float partial[32];
uint lane = tid % 32, warp = tid / 32;
if (warp == 0) partial[lane] = 0;
threadgroup_barrier(mem_flags::mem_threadgroup);
if (lane == 0) partial[warp] = sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
threadgroup float inverse;
if (warp == 0) {
float s = simd_sum(partial[lane]);
if (lane == 0) inverse = metal::precise::rsqrt(s/float(width)+epsilon[0]);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
uint index = indices[row]*width;
for (uint r = 0; r < width; r += TG*4) {
#pragma unroll
for (uint j = 0; j < 4; ++j) {
uint c = r+tid*4+j;
if (c < width) {
T affine = T(float(weight[c])*(float(x[row*width+c])*inverse));
T factor = T(T(1)+scale[index+c]);
T product = T(affine*factor);
out[row*width+c] = T(product+shift[index+c]);
}
}
}
""",
)
def norm_native_reduce(x, weight, shift, scale, indices, eps=1e-5, threads=1024):
return _norm()(
inputs=[
x,
weight,
shift,
scale,
indices.astype(mx.uint32),
mx.array([eps], mx.float32),
],
template=[("T", x.dtype), ("TG", threads)],
grid=(threads, x.shape[0], 1),
threadgroup=(threads, 1, 1),
output_shapes=[x.shape],
output_dtypes=[x.dtype],
)[0]