decode-ops

Fused decode-step glue for LLM inference on CPUs, aarch64-first: RMSNorm with a fused residual add, in-place RoPE, SiLU-gate, greedy argmax, and deterministic temperature / top-k / top-p sampling. This is the non-matmul residue that owns a CPU decode step once the linears and attention are fast. Companions on the Hub: phanerozoic/bitnet-cpu and phanerozoic/quant-matmul (linears), phanerozoic/cpu-attn (int8 KV attention).

Each op replaces a chain of small torch CPU dispatches and their intermediate tensors with one call. RMSNorm accumulates the square sum in f32; RoPE precomputes its angle pairs in f64 per call and rotates q and k in place (both half-split and interleaved layouts); SiLU and softmax use a NEON polynomial exp with exp(0) == 1 exactly. Sampling follows the HF warper order (temperature, top-k, then top-p over the renormalized survivors) with an explicit uniform draw, so a fixed generator gives a fixed token: survivors pop from a heap in descending logit order, ties to the lower index, and the token is the inverse CDF at the draw.

RMSNorm, the residual add, and SiLU-gate run on NEON (aarch64) or AVX2 + FMA (x86-64), selected once at runtime; the sampling and argmax exit paths are scalar. DO_CPU_ISA (scalar, avx2, neon) demotes for A/B runs.

Usage

import torch
from kernels import get_kernel

do = get_kernel("phanerozoic/decode-ops", version=1, trust_remote_code=True)

h = do.add_rmsnorm(residual, x, norm_weight)      # residual += x, then norm
do.rope(q, k, pos)                                # in place
y = do.silu_mul(gate, up)
tok = do.sample(logits, temperature=0.8, top_k=50, top_p=0.95,
                generator=gen)                    # deterministic given gen
tok = do.argmax(logits)                           # greedy

API

Function Purpose
rmsnorm(x, weight, eps) x * w / sqrt(mean(x^2) + eps)
add_rmsnorm(residual, x, weight, eps) residual += x in place, then norm
rope(q, k, pos, theta=10000, interleaved=False) rotate one position in place
silu_mul(gate, up) silu(gate) * up
argmax(logits) greedy token, first index on ties
sample(logits, temperature, top_k, top_p, generator) deterministic sampling; temperature <= 0 is greedy

Performance

Raspberry Pi 5 (4x Cortex-A76 2.4 GHz) and Raspberry Pi 4 Model B (4x Cortex-A72 1.8 GHz), 64-bit Raspberry Pi OS, torch 2.13 CPU, against the equivalent torch op sequences:

op Pi 5 torch Pi 4 torch
rmsnorm (1 x 4096) 16 us 38 us 66 us 171 us
add_rmsnorm (1 x 4096) 19 us 46 us 75 us 171 us
silu_mul (1 x 6912) 23 us 33 us 83 us 121 us
rope (32 + 8 heads x 128) 5.4 us - 15 us -
sample (V=128256, k=50, p=0.95) 1.6 ms 6.8 ms 4.5 ms 14.6 ms
argmax (V=128256) 0.11 ms 0.60 ms 0.18 ms 0.96 ms

Requirements

  • f32 activations (bf16 inputs are converted at the wrapper).
  • Sampling reproduces the HF warper order; results are deterministic given the generator state.
  • x86-64 runs the scalar path: correct, not tuned.

Scope

The glue half of a CPU decode stack. The roadmap item is a fused decoder_layer_forward chaining norm, linears, RoPE, attention, and the FFN through the companion kernels in one call per layer.

License

Apache-2.0.

Downloads last month
-
apache-2.0
OS
linux
Arch
x86_64aarch64
Kernel Builder
19aaa64