Register blocking
Register blocking holds an MR by NR accumulator in vector registers, cutting loads per MAC; DRAM intensity is the cache tile's job.
The tile that stays in registers
Tiling decided which tile a core works on. Register blocking decides how much of that tile stays in vector registers across the whole K loop. You hold an MR by NR accumulator tile, stream K, and touch memory once per K step for each row and each column you keep:
for (int k = 0; k < K; ++k) {
vfloat32m1_t b = __riscv_vle32_v_f32m1(B + k * N + j, vl);
for (int m = 0; m < MR; ++m)
acc[m] = __riscv_vfmacc_vf_f32m1(acc[m], A[(i + m) * K + k], b, vl);
}
Each acc[m] is one register group holding vl elements of one output row, so NR equals vl here. MR rows ship in MR groups, and the B vector is loaded once and multiplied against every row.
What the tile buys and what it costs
Per K step you move MR + NR elements and issue MR * NR multiply-accumulates, so loads per MAC fall as 1/MR + 1/NR. Doubling both dimensions halves load traffic per MAC. That is the entire return, and it is why the tile shape matters more than the tile size.
The cost is registers. A 4 by 32 f32 tile at vlen=256 is 128 accumulator elements, which is 16 of the 32 vector registers at e32,m1 before a B vector exists. The outer cache tile still decides what reaches L2; register blocking only decides what reaches the vector unit.
Things that break
The tile does not fit and the compiler spills vector registers to the stack inside the K loop. Look for vs1r.v and vl1re32.v against stack addresses between the vfmacc instructions in llvm-objdump -d. Results stay correct, so the loss looks like a bad tile choice rather than a register budget. Counting the groups before you compile is cheaper than discovering it in disassembly: one group per accumulator row, one for B, and whatever the epilogue keeps live.
Sources
- Anatomy of High-Performance Matrix Multiplicationpaper · ACM TOMS 2008
- MLIR Vector dialect documentationdocs · LLVM 23
- LLVM RISC-V Vector Extension guidedocs · LLVM 23
A version records the documentation the claim was checked against, not a house pin. Confirm flags with --help on the release you installed.
Check yourself
What are the loads per MAC for an `MR` by `NR` register tile?
A register-blocked matmul spills. Where does that show first?
What does the outer cache tile still control after register blocking?