cera 0.2.6

Rust-native LLM inference engine
Documentation
// RMSnorm: x = x / rms(x) * weight
//
// Single-workgroup approach for hidden_size ≤ 8192.
// Phase 1: parallel sum of squares → Phase 2: normalize + scale.
//
// Bind group 0:
//   @binding(0) x: array<f32>       (read-write, normalized in-place)
//   @binding(1) weight: array<f32>  (read-only, per-element scale)
//   @binding(2) params: vec4<u32>   (n, eps_bits, 0, 0)
//
// Dispatch: (1, 1, 1) — single workgroup

#define WG_SUM_REDUCE
#include "common_decls.tmpl"

@group(0) @binding(0) var<storage, read_write> x: array<f32>;
@group(0) @binding(1) var<storage, read> weight: array<f32>;
@group(0) @binding(2) var<storage, read> params: vec4<u32>;

var<workgroup> shared_sum: array<f32, 256>;

@compute @workgroup_size(256, 1, 1)
fn rmsnorm(@builtin(local_invocation_id) lid: vec3<u32>) {
    let tid = lid.x;
    let n = params.x;
    let eps = bitcast<f32>(params.y);

    // Phase 1: each thread computes partial sum of squares
    var partial: f32 = 0.0;
    var i = tid;
    while i < n {
        let v = x[i];
        partial += v * v;
        i += 256u;
    }
    shared_sum[tid] = partial;
    workgroupBarrier();

    // Parallel reduction
    workgroup_sum_reduce(tid);

    // Phase 2: normalize x[i] = x[i] * inv_rms * weight[i]
    let rms = sqrt(shared_sum[0] / f32(n) + eps);
    let inv_rms = 1.0 / rms;

    i = tid;
    while i < n {
        x[i] = x[i] * inv_rms * weight[i];
        i += 256u;
    }
}