Project Overview
Track: Cloud AI. An ISA-level dispatch audit for llama.cpp + KleidiAI on Neoverse server cores (Graviton / Cobalt / Axion class), reproducible on free Arm CI.
Neoverse N2 and Azure Cobalt 100 advertise sve sve2 svei8mm svebf16. Read /proc/cpuinfo on one and you will believe the SVE GEMM kernels are doing your matrix multiplies.
They are not.
KleidiAI gates every SVE kernel behind a runtime check for a 256-bit vector:
// llama.cpp - ggml/src/ggml-cpu/kleidiai/kleidiai.cpp
if (svcntb() == 32) { features |= CPU_FEATURE_SVE; }
svcntb() returns the SVE vector length in bytes, so 32 means 256-bit. N2 and Cobalt are 128-bit parts. svcntb() returns 16, CPU_FEATURE_SVE is never set, and every *_sve_* kernel in the dispatch table is unreachable dead code on the most widely-available Neoverse silicon - including the free GitHub Actions runner every developer can reach.
Measured on ubuntu-24.04-arm, not asserted:
SVE present : yes
svcntb() : 16 bytes (128-bit vector)
FEATURE ADVERTISED KLEIDIAI NOTE
dotprod (i8 dot) yes USES NEON path, always reachable
i8mm (int8 mm) yes USES neon_i8mm kernels - the real path
bf16 yes USES bf16 kernels where present
SVE / SVE2 yes refuses ADVERTISED BUT UNREACHABLE - kleidiai.cpp:209 needs svcntb()==32
=== VERDICT ===
This core advertises SVE, but KleidiAI will NOT dispatch a single SVE kernel.
Required: svcntb() == 32 (256-bit). Actual: 16 (128-bit).
Every *_sve_* GEMM kernel in the dispatch table is dead code here.
Your matmul is running the neon_i8mm path.
Why it should win
Every "deploy an LLM on Arm with llama.cpp and KleidiAI" guide tells you to check /proc/cpuinfo for SVE2. On the server parts most developers can actually rent, and on the CI runner they get for free, that flag describes a code path the process will never enter. The optimization you believe you enabled is not running, and nothing warns you.
This entry is not a wall-clock speedup claim. It is an ISA-level dispatch audit with a root cause and an exact source line, reproducible by a judge on their own free runner in about two minutes without hardware or cloud spend. It reverses an assumption the whole field is operating under, and it is checkable in 30 seconds by anyone who knows the code.
Honesty guardrails
An Arm engineer will check these, so they are stated up front rather than buried:
- The gate is KleidiAI's, not ggml's. ggml proper does use SVE elsewhere (
vec.cpp,simd-gemm.h). The claim is scoped to KleidiAI's kernel dispatch. "ggml ignores SVE" would be false. - The SVE kernels exist. There is a
..._16x8_sve_i8mmGEMM kernel in the table. The claim is that it is unreachable on 128-bit cores, not that it is absent. - The portable build refuses to guess.
svcntb()is an SVE intrinsic, so a build without+svecannot read the vector length. Rather than print a fabricated0, it reportsunknown, prints the rebuild command, and exits 3. Only the+svebuild renders a verdict. This was a real bug caught by CI on the first run, when the portable build printedActual: 0 (0-bit)- a number it had not measured. - Apple Silicon has no SVE at all. On an M2 the probe exits 0 with "the NEON/i8mm path is the only path", which is why benchmark numbers from SVE-capable hardware transfer nowhere.
What this optimizes, and the before/after
The reminder asks what was optimized. This project's target is the developer's optimization decision itself - the step before any latency number is meaningful.
Before: you read /proc/cpuinfo, see sve sve2 svei8mm svebf16, follow an Arm Learning Path, and benchmark. You attribute your throughput to the SVE i8mm kernels. Any tuning you do from there is aimed at a code path your process never enters.
After (measured, ubuntu-24.04-arm, run 30711783573):
| Before (assumed) | After (measured) | |
|---|---|---|
| Kernel family in use | *_sve_i8mm GEMM |
neon_i8mm GEMM |
| SVE vector length | assumed >= 256-bit | svcntb() = 16 bytes = 128-bit |
CPU_FEATURE_SVE |
assumed set | never set |
| SVE kernels reachable | assumed yes | zero of them |
| How you find out | you do not | ./isa-probe-sve; echo $? -> 2 |
The delta this produces is not a percentage on a token rate - it is that every SVE-targeted optimization on 128-bit Neoverse has an expected speedup of exactly zero, and this tells you so in two minutes instead of after a week of tuning. On hardware that is 256-bit, the same probe exits 0 and your SVE work is real.
Cross-checked on Apple M2 (no SVE at all, exits 0) so the comparison is honest about which ISA did what.
Functionality / Output
isa-probe prints what the CPU advertises next to what KleidiAI will actually dispatch, then renders a verdict with a distinct exit code so it drops straight into CI as a gate:
| Exit | Meaning |
|---|---|
0 |
No SVE on this core, or SVE present and 256-bit (KleidiAI would use it) |
2 |
Advertised but refused - the core has SVE, KleidiAI will not dispatch it |
3 |
Advertised, but this build cannot measure the vector length (rebuild with +sve) |
The GitHub Actions workflow additionally:
- prints the runner's own
/proc/cpuinfofeature line, core count and memory, so the hardware is on the record - builds both the portable and the
+svevariant, and records it when the toolchain cannot target SVE rather than hiding it - clones upstream llama.cpp and greps
svcntbout of the real source, so the 256-bit threshold is read from the code rather than taken from this description - if upstream changes the constant, the step surfaces it - enables PMU counters (
kernel.perf_event_paranoid=-1) and runsperf statto confirm real hardware counters are live - uploads both binaries as an artifact
Setup Instructions
Reproduce on a free Neoverse runner (no hardware, no cloud spend)
- Fork
https://github.com/luongs3/arm-dispatch-audit - Actions tab, select arm-dispatch-audit, click Run workflow
- Open the step named "The finding - advertised vs dispatched"
The runner is ubuntu-24.04-arm (Neoverse N2 / Azure Cobalt 100), free on public repositories, 4 vCPU / 15 GB. Expect the +sve build to report svcntb() : 16 bytes (128-bit vector) and exit 2.
Reproduce locally on any Arm machine
git clone https://github.com/luongs3/arm-dispatch-audit
cd arm-dispatch-audit
# The build that can actually answer the question:
cc -O2 -march=armv8.2-a+sve -DHAVE_SVE -o isa-probe-sve src/isa-probe.c
./isa-probe-sve; echo "exit: $?"
# The portable build (works anywhere, declines to guess about SVE):
cc -O2 -o isa-probe-portable src/isa-probe.c
./isa-probe-portable; echo "exit: $?"
No dependencies beyond a C compiler. On Apple Silicon the +sve build is unnecessary - there is no SVE to measure - and the probe will say so.
Validate the claim against upstream yourself
git clone --depth 1 https://github.com/ggml-org/llama.cpp
grep -rn "svcntb" llama.cpp --include=*.cpp --include=*.h
You should find the svcntb() == 32 comparison that gates CPU_FEATURE_SVE.
Repository
https://github.com/luongs3/arm-dispatch-audit (public, Apache-2.0)
Log in or sign up for Devpost to join the conversation.