For the complete documentation index, see llms.txt. Markdown versions of all pages are available by appending .md to any URL (e.g. /max/get-started.md).
Mojo struct
AppleM5MatMul
struct AppleM5MatMul[in_type: DType, c_type: DType = DType.float32, transpose_b: Bool = False, elementwise_lambda_fn: Optional[def[dtype: DType, width: SIMDLength, *, alignment: Int = Int(1)](IndexList[Int(2)], SIMD[dtype, width]) capturing thin -> None] = None, block_m: Int = Int(64), block_n: Int = Int(64), block_k: Int = Int(16), sg_m: Int = Int(32), sg_n: Int = Int(32), k_unroll: Int = Int(1), use_x2: Bool = False, linear_idx_type: DType = DType.int64, clamp_edge: Bool = False, b_type: DType = in_type, accum_type: DType = DType.float32]
Apple M5 simdgroup-tiled GEMM (Metal 4 hardware MMA).
run is the GPU kernel entry (TileTensor operands; M/N/K derived from
them). run_split_k_partial / run_split_k_reduce are the split-K kernels
(bf16 family; split-K is not wired for b_type != in_type). Launch via
enqueue_apple_matmul / enqueue_apple_matmul_split_k; the FP8 W8A16 dense
launcher is enqueue_matmul2d_fp8.
Parametersβ
- βin_type (
DType): A element type (fp16, bf16, fp32; int8 at the MMA-op level). - βc_type (
DType): Output element type (fp16, bf16, fp32). Accumulation isaccum_type. - βtranspose_b (
Bool): If True, B is(N, K)row-major (viewed ascol_major(K, N)); otherwise B is(K, N)row-major. - βelementwise_lambda_fn (
Optional[def[dtype: DType, width: SIMDLength, *, alignment: Int = Int(1)](IndexList[Int(2)], SIMD[dtype, width]) capturing thin -> None]): Optional fused epilogue; receivesSIMD[c_type, width]at absolute(row, col)(AMD's contract). - βblock_m (
Int): Threadgroup block rowsBM(M tile). Multiple ofSG_M. - βblock_n (
Int): Threadgroup block colsBN(N tile). Multiple ofSG_N. - βblock_k (
Int): K-strip depthBKper accumulate step. Multiple ofMMA_K(16). The conv path (enqueue_apple_conv2d) overrides these to tune the fused im2col GEMM independently of the dense GEMM defaults. - βsg_m (
Int): Simdgroup subtile rowsSG_M. Multiple ofMMA_M(16); fixes the M MMA-fragment countNUM_MMA_M = SG_M / 16. - βsg_n (
Int): Simdgroup subtile colsSG_N. Multiple ofMMA_N(16); fixes the N MMA-fragment countNUM_MMA_N = SG_N / 16. - βk_unroll (
Int): Denserunpath interior K-loop unroll factor (default 1, no unroll). Processesk_unrollBK-strips per loop pass, amortizing loop-control overhead across the group. Regresses severely if combined withuse_x2(register pressure) -- the dispatcher never sets both. - βuse_x2 (
Bool): NT bf16/fp16 "double-strip" dense MMA (block_k=32,MmaOpApple.mma_dense_x2): both operands are K-contiguous, so one call covers 32 K, halving the K-loop iteration count. - βlinear_idx_type (
DType): Integer type for the denserunpath's A/B/CTileTensoroffset arithmetic (Apple's scalar ALU is faster on 32-bit). Only safe when every offset provably fits int32. - βclamp_edge (
Bool): Enablesclamp_v2: shift a ragged last M/N tile tom-BM/n-BNfor a full, fast load, then store back only its owned region so it doesn't overwrite the previous tile. Dense fp32 only, noelementwise_lambda_fn. Onlyrun_chainedconsumes it;run/run_convnever set it. - βb_type (
DType): B/weight operand dtype fed to the MMA. Defaults toin_type(the dense/conv case). The W8A16 FP8 path setsb_type=float8_e4m3fn: the A slab staysin_type(bf16) and only the B fragment widens to the native-fp8 MMA operand (the weight-loader seam swap). SeeWeightLoader/enqueue_matmul2d_fp8. - βaccum_type (
DType): MMA accumulator dtype. fp32 for dense/fp8; a later int8 slice uses int32.
Implemented traitsβ
comptime membersβ
BKβ
comptime BK = block_k
BMβ
comptime BM = block_m
BNβ
comptime BN = block_n
Mmaβ
comptime Mma = MmaOpApple[accum_type, in_type, AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].NUM_MMA_M, AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].NUM_MMA_N, b_type=b_type]
NUM_MMA_Mβ
comptime NUM_MMA_M = (AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].SG_M // Int(16))
NUM_MMA_Nβ
comptime NUM_MMA_N = (AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].SG_N // Int(16))
NUM_SGβ
comptime NUM_SG = (AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].NUM_SG_M * AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].NUM_SG_N)
NUM_SG_Mβ
comptime NUM_SG_M = (AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].BM // AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].SG_M)
NUM_SG_Nβ
comptime NUM_SG_N = (AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].BN // AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].SG_N)
REDUCE_BLOCKβ
comptime REDUCE_BLOCK = 256
SG_Mβ
comptime SG_M = sg_m
SG_Nβ
comptime SG_N = sg_n
THREADS_PER_BLOCKβ
comptime THREADS_PER_BLOCK = (AppleM5MatMul[in_type, c_type, transpose_b, elementwise_lambda_fn, block_m, block_n, block_k, sg_m, sg_n, k_unroll, use_x2, linear_idx_type, clamp_edge, b_type, accum_type].NUM_SG * _resolve_warp_size())
Methodsβ
morton_decode_2dβ
static def morton_decode_2d(flat_idx: UInt32) -> Tuple[UInt32, UInt32]
Decode a linear index to (tile_m, tile_n) via Morton Z-order.
Even bits of flat_idx -> tile_n, odd bits -> tile_m. The decoded pair may fall outside any rectangular grid that isn't a power-of-2 square; the caller checks bounds.
Future: as of AIR 4.1 a bit_interleave intrinsic can do this
interleave directly, replacing the hand-rolled bit-scatter below. A
Gilbert-curve dispatch order was also explored for better locality,
but it needs a per-shape predispatch kernel to generate the order.
Args:
- βflat_idx (
UInt32): The linear threadgroup index to split into interleaved Z-order bits; even bits formtile_n, odd bits formtile_m.
Returns:
morton_decode_2d_rectβ
static def morton_decode_2d_rect(flat_idx: UInt32, log2_m: UInt32, log2_n: UInt32) -> Tuple[UInt32, UInt32]
Decode flat_idx to (tile_m, tile_n) over a (1<<log2_m) x (1<<log2_n) grid.
Z-order covers a min(side_m, side_n) square core; remaining bits sweep
the longer axis. Reduces to morton_decode_2d when log2_m == log2_n.
Args:
- βflat_idx (
UInt32): The linear threadgroup index to decode into a 2-D tile coordinate. - βlog2_m (
UInt32): Base-2 logarithm of the grid's M extent; the decodedtile_mranges over[0, 1<<log2_m). - βlog2_n (
UInt32): Base-2 logarithm of the grid's N extent; the decodedtile_nranges over[0, 1<<log2_n).
Returns:
runβ
static def run[c_layout: TensorLayout, a_layout: TensorLayout, b_layout: TensorLayout, c_storage: TensorStorage, a_storage: TensorStorage, b_storage: TensorStorage](c: TileTensor[c_type, c_layout, MutAnyOrigin, Storage=c_storage], a: TileTensor[in_type, a_layout, ImmutAnyOrigin, Storage=a_storage], b: TileTensor[b_type, b_layout, ImmutAnyOrigin, Storage=b_storage], log2_grid_m: UInt32, log2_grid_n: UInt32)
GEMM kernel entry; M/N/K derive from the operands.
A is (M, K) row-major and C is (M, N) row-major; B is (K, N) for
transpose_b=False or (N, K) for transpose_b=True. Grid is
(1<<log2_grid_m) * (1<<log2_grid_n) threadgroups of 128 threads; OOB
threadgroups early-return after Morton decode.
Thin wrapper over _run_gemm_body: derives K, pre-tiles this
simdgroup's (SG_M, K) A slab, and constructs the DenseALoader that
reads it.
Parameters:
- βc_layout (
TensorLayout):TensorLayoutof the outputCoperand. - βa_layout (
TensorLayout):TensorLayoutof the A operand. - βb_layout (
TensorLayout):TensorLayoutof the B operand. - βc_storage (
TensorStorage):TensorStorageof the outputCoperand. - βa_storage (
TensorStorage):TensorStorageof the A operand. - βb_storage (
TensorStorage):TensorStorageof the B operand.
Args:
- βc (
TileTensor[c_type, c_layout, MutAnyOrigin, Storage=c_storage]): Output matrix(M, N)row-major;MandNderive from its dims. - βa (
TileTensor[in_type, a_layout, ImmutAnyOrigin, Storage=a_storage]): A operand matrix(M, K)row-major;Kderives froma.dim[1]. - βb (
TileTensor[b_type, b_layout, ImmutAnyOrigin, Storage=b_storage]): B operand matrix,(K, N)fortranspose_b=Falseor(N, K)fortranspose_b=True. - βlog2_grid_m (
UInt32): Base-2 logarithm of the M-axis grid extent; the grid spans1<<log2_grid_mthreadgroups along M. - βlog2_grid_n (
UInt32): Base-2 logarithm of the N-axis grid extent; the grid spans1<<log2_grid_nthreadgroups along N.
run_chainedβ
static def run_chained[c_layout: TensorLayout, a_layout: TensorLayout, b_layout: TensorLayout, c_storage: TensorStorage, a_storage: TensorStorage, b_storage: TensorStorage, seed_from_output: Bool = False](c: TileTensor[c_type, c_layout, MutAnyOrigin, Storage=c_storage], a: TileTensor[in_type, a_layout, ImmutAnyOrigin, Storage=a_storage], b: TileTensor[b_type, b_layout, ImmutAnyOrigin, Storage=b_storage], log2_grid_m: UInt32, log2_grid_n: UInt32, k_strip_start: Int, k_strip_end: Int)
clamp_v2 chained-pass kernel. enqueue_apple_matmul_clamp_chain launches it twice: pass 0 zero-seeds [k_strip_start, k_strip_end), pass 1 seeds from c and covers the rest, so no partials buffer or reduce kernel is needed. Otherwise identical to run; requires Self.clamp_edge=True (asserted below).
run_convβ
static def run_conv[c_layout: TensorLayout, input_layout: TensorLayout, b_layout: TensorLayout, c_storage: TensorStorage, input_storage: TensorStorage, b_storage: TensorStorage, c_aligned: Bool = False](c: TileTensor[c_type, c_layout, MutAnyOrigin, Storage=c_storage], input: TileTensor[in_type, input_layout, ImmutAnyOrigin, Storage=input_storage], b: TileTensor[in_type, b_layout, ImmutAnyOrigin, Storage=b_storage], conv: ConvIm2colParams, log2_grid_m: UInt32, log2_grid_n: UInt32)
Fused conv2d GEMM entry; M/N/K derive from C and the conv params.
C is (M, N) row-major with M = N_batch*H_out*W_out, N = C_out.
input is the 4-D NHWC source (flat pointer used for the gather). B is
the filter (N, K) with transpose_b=True (the NK layout the existing
matmul path uses). Requires transpose_b == True. Grid is identical to
run. The fused epilogue (if any) and the partial-edge bounds are the
same as run -- only the A-fragment producer changes.
Thin wrapper over _run_gemm_body: derives K = R*S*C and constructs an
Im2colALoader over input (no slab -- the gather reads NHWC directly).
conv is threaded to the body as a value arg (NOT held in the loader --
see Im2colALoader for the Metal addrspace reason), then runs the body.
Parameters:
- βc_layout (
TensorLayout):TensorLayoutof the outputCoperand. - βinput_layout (
TensorLayout):TensorLayoutof the NHWCinputoperand. - βb_layout (
TensorLayout):TensorLayoutof the filterBoperand. - βc_storage (
TensorStorage):TensorStorageof the outputCoperand. - βinput_storage (
TensorStorage):TensorStorageof the NHWCinputoperand. - βb_storage (
TensorStorage):TensorStorageof the filterBoperand. - βc_aligned (
Bool): When True, assumeconv.Cis a multiple of 8 so the channel run is contiguous and a single width-8 load suffices (defaults to False).
Args:
- βc (
TileTensor[c_type, c_layout, MutAnyOrigin, Storage=c_storage]): Output matrix(M, N)row-major withM = N_batch*H_out*W_outandN = C_out. - βinput (
TileTensor[in_type, input_layout, ImmutAnyOrigin, Storage=input_storage]): 4-D NHWC source tensor; its flat pointer drives the im2col gather. - βb (
TileTensor[in_type, b_layout, ImmutAnyOrigin, Storage=b_storage]): Filter operand(N, K)withtranspose_b=True(NK layout). - βconv (
ConvIm2colParams): Conv geometry for the im2col gather; threaded to the body as a value arg. - βlog2_grid_m (
UInt32): Base-2 logarithm of the M-axis grid extent; the grid spans1<<log2_grid_mthreadgroups along M. - βlog2_grid_n (
UInt32): Base-2 logarithm of the N-axis grid extent; the grid spans1<<log2_grid_nthreadgroups along N.
run_split_k_partialβ
static def run_split_k_partial[a_layout: TensorLayout, b_layout: TensorLayout, a_storage: TensorStorage, b_storage: TensorStorage](partials_ptr: Pointer[Float32, MutAnyOrigin, _safe=False], a: TileTensor[in_type, a_layout, ImmutAnyOrigin, Storage=a_storage], b: TileTensor[in_type, b_layout, ImmutAnyOrigin, Storage=b_storage], log2_grid_m: UInt32, log2_grid_n: UInt32, k_per_split: Int)
One 64x64 output tile's fp32 partial over a BK-aligned K-slice.
Grid is (side_m * side_n) * num_splits threadgroups. The split index
selects the K-range [s*k_per_split, min(K, (s+1)*k_per_split)) and the
partials[s] output matrix; the tile index is Morton-decoded as in
run. k_per_split is a multiple of BK, so every split but the last
is full BK strips; the last may carry a partial-BK tail. No cast, no
epilogue -- raw fp32 accumulator out.
Parameters:
- βa_layout (
TensorLayout):TensorLayoutof the A operand. - βb_layout (
TensorLayout):TensorLayoutof the B operand. - βa_storage (
TensorStorage):TensorStorageof the A operand. - βb_storage (
TensorStorage):TensorStorageof the B operand.
Args:
- βpartials_ptr (
Pointer[Float32, MutAnyOrigin, _safe=False]): Pointer to the fp32 partials workspace; splitswrites its partial at offsets * M * N. - βa (
TileTensor[in_type, a_layout, ImmutAnyOrigin, Storage=a_storage]): A operand matrix(M, K)row-major;MandKderive from its dims. - βb (
TileTensor[in_type, b_layout, ImmutAnyOrigin, Storage=b_storage]): B operand matrix,(K, N)fortranspose_b=Falseor(N, K)fortranspose_b=True. - βlog2_grid_m (
UInt32): Base-2 logarithm of the M-axis grid extent; the grid spans1<<log2_grid_mthreadgroups along M. - βlog2_grid_n (
UInt32): Base-2 logarithm of the N-axis grid extent; the grid spans1<<log2_grid_nthreadgroups along N. - βk_per_split (
Int): K extent per split, a multiple ofBK; splitsowns[s*k_per_split, min(K, (s+1)*k_per_split)).
run_split_k_reduceβ
static def run_split_k_reduce[c_layout: TensorLayout, c_storage: TensorStorage](c: TileTensor[c_type, c_layout, MutAnyOrigin, Storage=c_storage], partials_ptr: Pointer[Float32, ImmutAnyOrigin, _safe=False], num_splits: Int)
Sum num_splits fp32 partials per output element, cast, store / fuse.
One thread per output element; idx = block_idx.x * block_dim.x + thread_idx.x. The fused elementwise_lambda_fn (if any) sees the
absolute (row, col) and the final SIMD[c_type, 1].
Parameters:
- βc_layout (
TensorLayout):TensorLayoutof the outputCoperand. - βc_storage (
TensorStorage):TensorStorageof the outputCoperand.
Args:
- βc (
TileTensor[c_type, c_layout, MutAnyOrigin, Storage=c_storage]): Output matrix(M, N)row-major;MandNderive from its dims. - βpartials_ptr (
Pointer[Float32, ImmutAnyOrigin, _safe=False]): Pointer to the fp32 partials workspace; splitspartial is at offsets * M * N. - βnum_splits (
Int): Number of K splits to sum per output element.
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!