Chapter 11

layers/dense — MatVec microkernel

github.com/openfluke/welvet/layers/dense


Why it exists

Most FLOPs are W@x. One Dense stack owns FormatNone×34 and all quants × three backends so every composite proj shares one correctness surface — including native in-dtype SGD.

What it is

New / NewConfigured[T], Forward/Backward (dispatch on Exec.Backend), Place, ApplyGradSGD (→ weights.ApplySGD on the store). SIMD forward (v1.0.3): dtype switch by MatVec strategy — DotTile / DotI8 / lowp packed / expand-once→DotTile / WireF64+DotTileF64; fused Dot* for classic Q*, k/IQ, AffinePacked. Composites (MHA, SwiGLU, CNN im2col, RNN/LSTM/Mamba, residual·sequential·parallel) reuse Dense children via syncProjExec. BackwardSIMD still DecodeRow/saxpy (not the new expand wires).

v1.0.3 — FormatNone SIMD forward Deep profile (batch=8, 8×256→256, SIMD-only, 34 dtypes): Go vs tiny C++/Rust ports, bit-identical hashes. Go refreshed kernels → suite ~4.3× geo-mean vs prior Go; wins flipped 2→24 (C++ 9, Rust 1). Forward only. Source numbers: welvet-to-rust_n_cpp.

forwardSIMDByWire (strategy groups)

casedtypeskernel
narrow i8int4, int2, ternary, binaryexpand → DotI8Tile
uint affineuint4/2/3/5/6, uint16…uintptrexpand once → DotTile
lowp packedf16, bf16, fp8, fp4convert tiles → DotTile
expand f32nf4, fp6, int3/5/6expand once → DotTile (exact)
stream f64float64, int16/32/64, int, complex*WireF64 → DotTileF64
SelectWirefloat32, int8, uint8, …DotTile / DotI8 / affine u8

Before → after (µs/op, Go Δ)

Full Go/C++/Rust columns: before wins Go 2 / C++ 31 / Rust 1; after Go 24 / C++ 9 / Rust 1. Highlight Go speedups (before→after):

dtypeGo beforeGo afterGo Δbest after
float321023.1929.51.10×go
float649565.41017.49.40×go
bfloat1612538.95045.32.49×cpp
int8708.1659.31.07×cpp
int6422271.01059.921×go
int3222495.4964.123×go
int1622147.5953.523×go
int / complex*~30k~1.0k~29–31×go
uint* / nf4 / fp6 / int3–6~20–77k~3.7–10.6k~5–7×go
uint84322.37575.00.57×cpp

C++ still owns int8/narrow, uint8, bf16/fp8. float32 DotTile remains Go’s race (929.5 vs C++ 1162 / Rust 1335). uint8 affine is a known Go follow-up.

Go example

examples/11-dense/main.go

Run:cd welvet/examples/11-dense && source ../env.sh && go run .
package main

import (
	"fmt"

	"github.com/openfluke/welvet/core"
	"github.com/openfluke/welvet/layers/dense"
	"github.com/openfluke/welvet/quant"
)

func main() {
	init := make([]float32, 4*8)
	l, err := dense.NewConfigured(8, 4, core.ActivationReLU, core.DTypeFloat32, quant.FormatNone, init)
	if err != nil {
		panic(err)
	}
	l.Exec.Backend = core.BackendCPUTiled
	x := core.NewTensor[float32](1, 8)
	pre, post, err := dense.Forward(l, x)
	if err != nil {
		panic(err)
	}
	gIn, gW, err := dense.Backward(l, post, x, pre)
	_ = dense.ApplyGradSGD(l, gW, 1e-3)
	fmt.Println(len(gIn.Data), len(gW.Data))
}

Output

exit 0 · last run via go run .

8 32