Microkernel quality
Microkernel quality shows in unroll and cleanup overhead, load reuse, and instruction mix, not only GFLOP/s.
The loop body is the review
GFLOP/s is one number about a kernel and the least diagnostic one. The K-loop body carries the rest: how many MACs sit between two loads, whether those loads are reused across rows, how many vsetvli instructions appear, and how much code exists outside the loop at all.
Separate the kernel into its own section and pull the body out:
llvm-objdump -d --no-show-raw-insn kernel.elf \
| sed -n '/<matmul_kernel>:/,/ret/p' > kernel.s
grep -c vfmacc kernel.s
grep -c vle32 kernel.s
Read the ratio rather than the counts. A blocked kernel approaches several MACs per load; parity between vfmacc and vle32 says the tile never reached the loop, whatever the tile parameter claims.
Unroll and cleanup
Unrolling shows as a repeated body with one backward branch, and it changes the branch-per-MAC ratio rather than the load ratio. Cleanup is different: the prologue, the epilogue and the scalar tail for ragged shapes are code that runs on some shapes and not others. Count instructions outside the loop and compare with the ones inside. A kernel with 40 instructions in the loop and 400 outside is a kernel whose small-shape performance is cleanup, and a shape sweep will show it as a step rather than a slope.
Scheduling tools
llvm-mca gives a throughput and a dependency view for a straight-line block, and it is useful for the scalar core. For the vector body, check what your release ships: llvm-mca -march=riscv64 -mcpu=<cpu> --help shows the flags this build takes, and the model is only as good as the scheduling information it carries for that core. Treat the output as a hint about dependency chains, not as a cycle count for RVV code.
Things that break
The loop reloads B every K step because nothing was hoisted, and the load ratio is the only trace: the timing still improves with tile size, so it looks like a tuning problem. The second failure is measuring only shape multiples. No tile-multiple shape executes the cleanup path, so a kernel with a slow tail reports a clean curve and a chip on it.
Sources
- llvm-objdump command guidedocs · LLVM 23
- llvm-mca command guidedocs · LLVM 23
- Google Benchmark user guidedocs
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
The inner loop is half loads and half MACs. What does that tell you?
Why report instruction counts next to GFLOP/s on the emulated rig?
A kernel is fast at every tile-multiple shape and falls off just below one. What is the first thing to check?