Skip to main content
Jonathan Andrei
Back to all posts
Aug. 202612 min read

coldpath: The Ollama Windows-on-Arm Build Ships with the Matrix Unit Off. One-Line Fix Filed Upstream, 5.75x on Prompt Processing.

I could not answer a basic question about my own Arm machine: when a local LLM runs, is it actually using the chip's matrix hardware, or silently falling back to scalar code? So I wrote the tool that answers it, pointed it at the ecosystem, and found the most popular Windows-on-Arm LLM runner cold. coldpath is a Capstone-based AArch64 disassembler that proves a binary contains SME, i8mm, and dotprod instructions; the finding is Ollama's official win-arm64 build has zero of any of them. One-line fix filed upstream (PR #17654), 5.75x recovered on prompt processing measured live on Azure Cobalt 100, gated in CI as a reusable GitHub Action.

ArmCloudAIAArch64Neoverse N2Azure Cobalt 100Ollamallama.cppggmlKleidiAICapstoneGitHub ActionsPython
I created this post and the coldpath project for the Arm Cloud AI challenge on Devpost. The upstream fix landed as Ollama PR #17654 during the challenge window.
The claim in one sentence: Ollama's official Windows-on-Arm build (the most popular way to run a local LLM) executes zero matrix and zero dot-product instructions, and any portable Arm build that misses one compile flag does the same. On Azure Cobalt 100 that flag is worth 5.75x on prompt processing and about $0.45 vs $0.08 per million prompt tokens, measured live on the free GitHub Neoverse N2 runner. coldpath finds it in any shipped binary with no Arm hardware, the fix I filed is one line, and the gate is a reusable GitHub Action so it cannot come back.
Terminal output of `coldpath ollama-windows-arm64/lib/ollama/ggml-cpu.dll` showing 154,880 instructions decoded at 100% coverage with the COLD verdict and zeros for SME (ZA tile), i8mm (smmla), and dotprod (sdot). The matrix hardware is switched off.
Ollama v0.31.2, official Windows-on-Arm release, exactly as it ships. Zero matrix instructions, zero dot-product instructions, at 100% decode coverage. Absence is airtight: no core can ever run an i8mm matmul on this binary, however the library dispatches at runtime.

The question I could not answer

I wanted to know something basic about my own setup: when I run a quantized LLM on an Arm machine, is it actually using the chip's matrix-multiply instructions, or silently falling back to scalar code? I could not find a way to answer it. Arm's matrix kernels (i8mm, SME2, dot-product) are selected at compile time, so a wrong build flag does not make them slow, it removes them from the binary entirely, and nothing warns you. The only advice on offer was to attach a profiler to real Arm silicon and read assembly symbol names by hand. That gap is the whole project: a question a competent engineer cannot answer today, made answerable, with no Arm hardware required.

Why a static scan is sufficient (and works on Arm but not x86)

ggml, XNNPACK and most Arm kernel libraries bake their fast paths into each binary at compile time. After llama.cpp PR #10457 (which removed runtime ISA detection to fix a ~15x regression), the i8mm/dotprod intrinsics are `#if`-guarded on `__ARM_FEATURE_MATMUL_INT8` and `__ARM_FEATURE_DOTPROD`. So if the build used the wrong `-march`, the fast path is not merely skipped: it is compiled out, physically absent from the binary. Disassembling `.text` and looking is therefore decisive. Two properties make this sound. First, AArch64 is fixed-width, 4-byte instructions on a 4-byte grid, so a data word embedded in `.text` (a literal pool, a jump table) is local to its own 4 bytes and can never cascade into the surrounding code. An x86 linear sweep would desynchronise and corrupt everything downstream; on Arm it cannot. Second, detection reads register operands (ZA tiles for SME, Z/P for SVE) because Capstone leaves `insn.groups` empty for both SVE and SME, so group-based detection silently reports zero, the exact false-negative class this tool exists to catch.

Side-by-side coldpath output on the same OS with the same ggml source: llama.cpp's own win-arm64 release is WARM with i8mm 244 and dotprod 1,052, while Ollama's win-arm64 release is COLD with i8mm 0 and dotprod 0. One build flag apart.
The comparison that makes the flag the cause. Same OS, same ggml source, two shipped binaries one build flag apart. Ollama does not fork ggml: it builds pinned upstream llama.cpp with one flag missing. This is a distribution hazard, not Ollama being incapable.

What the cold path costs, in dollars, measured live

The honest part. Every row is built `GGML_NATIVE=OFF` with a pinned `-march` to isolate the ISA effect; only `-march` changes. Cold armv8-a (the baseline a portable build falls back to, Ollama's Windows build) runs Qwen2.5-0.5B Q4_0 prefill at ~95 tok/s. The one-line fix I filed (armv8.2-a+dotprod, safe on every shipped Arm device) runs at ~545, a 5.75x speedup, about $0.08 per million prompt tokens against a $0.45 baseline. armv8.6-a+i8mm+bf16 runs at ~660, 6.9x, about $0.065. Generation improves about 2.3x. The dollars use a sample Arm-cloud on-demand rate (Graviton4 c8g, $0.0385/vCPU-hr) so only that column assumes a price; the tok/s and the 5.75x are measured on the 4-vCPU Neoverse N2 runner and regenerate from CI on every push.

Cost chart titled 'What the cold path costs', measured live on Azure Cobalt 100 (Neoverse N2): cold armv8-a runs at ~95 tok/s at $0.45 per 1M tokens, the +dotprod fix runs at ~545 tok/s at $0.08 per 1M tokens, a 5.75x speedup. Only the -march flag changes between rows.
$0.45 to $0.08 per million prompt tokens, on the free GitHub Arm64 runner. The dot-product fix is safe on every shipped Arm device, so this recovery is universal, not conditional on newer silicon.
GitHub Actions summary of the 'What the cold path costs' workflow: three ISA builds (COLD armv8-a, TEPID armv8.2-a+dotprod, WARM armv8.6-a+i8mm+bf16) plus a compare job, all successful on ubuntu-24.04-arm.
Every number regenerates from CI on free Arm hardware, so a judge can re-run it rather than trust a screenshot. Fork the repo and hit run on the workflow.

The upstream fix: one line, filed as Ollama PR #17654

The cause is a single missing CMake variable in Ollama's `llama/server/CMakePresets.json`. `GGML_CPU_ALL_VARIANTS` is off (so the build cannot dlopen the best of several ISA variants at runtime), and `GGML_CPU_ARM_ARCH` is unset, so the build falls back to baseline armv8-a and every matrix intrinsic gets `#if`-ed out. Adding `"GGML_CPU_ARM_ARCH": "armv8.2-a+dotprod"` recovers dot-product on every shipped Arm device, including the Cortex-A76-class chips in Windows-on-Arm laptops that do not have i8mm. That is why the PR intentionally ships dot-product only, not the 6.9x i8mm row: dot-product is safe on every core, i8mm is not.

Diff view of llama/server/CMakePresets.json showing the one-line addition: `"GGML_CPU_ARM_ARCH": "armv8.2-a+dotprod"`, added alongside the existing GGML_CPU_ALL_VARIANTS OFF and OLLAMA_WINDOWS_RUNTIME_ARCH arm64 keys.
The whole fix. One line, filed as Ollama PR #17654 during the challenge window. Confirmed COLD on all 8 stable Ollama releases from v0.31.2 through the current v0.32.7, so this claim cannot silently rot: `test.yml` re-downloads the latest release and re-checks it on every push.

As a CI gate: a cold build never reaches your fleet

Point the reusable Action at your own build output, where every binary should be hot. `coldpath --require i8mm ./build/` exits non-zero if any shipped binary lacks the matrix instructions, so the pull request fails before the cold build reaches an Arm fleet. When you instead scan a whole third-party release archive, the matmul kernels usually live in one backend library (e.g. `ggml-cpu.dll`) while the `llama-*`/`ollama` executables next to it are thin launcher shims that carry no kernels, so a strict gate would fail on the shims. For that case judge the set by its best member with `--any` (CLI) or `any: true` (Action). The same flag covers a multi-variant build that dlopens the best of several single-ISA libraries at runtime, so the set is scored on the variant that actually loads, not its armv8.0 fallback.

Simulated CI output of `coldpath --require i8mm ./build/` reporting '1 of 1 binaries lack i8mm: - lib/ollama/ggml-cpu.dll' with a red 'PR failed' verdict and the note 'a cold build never reaches your fleet'.
The gate is a reusable GitHub Action published from this repo: `uses: JonathanSolvesProblems/coldpath@v1` with `require: i8mm`. That is all it takes to block a cold build.

Ground truth, and honest limitations

The tool is validated against ground truth it did not author. llama.cpp's official linux-arm64 release ships eight `ggml-cpu` backends with the ISA level in the filename, and `scripts/verify_ladder.py` reproduces that eight-row staircase exactly (armv8.0 has zero of everything, armv8.2 gets 1,184 dotprod, armv8.2_3 adds 10,735 SVE, armv8.6 adds 402 i8mm, and so on). ONNX Runtime's aarch64 wheel is a positive control proving a zero means a real absence, with 469 SME, 800 i8mm, and 1,642 dotprod. Scope is stated up front: on Apple Silicon, matmul can route through the Accelerate/AMX unit, which a disassembler cannot see, so the headline is scoped to Linux/Neoverse, Windows-on-Arm, and Android where the ISA path is the only path. coldpath reports what is present in `.text`; presence equals what executes for ggml (compile-time dispatch) and is a proven-shipped indicator for runtime-dispatch libraries.

coldpath brand card: the wordmark 'coldpath' in violet with the tagline 'does your Arm AI binary use the matrix hardware?' and the install command `$ pip install coldpath`. Below: 'MIT · on PyPI · found a real one on day one'.
`pip install coldpath`, MIT, on PyPI. The tool found a real defect in software millions of people run, on its first day. Next up: distinguishing SME from SME2, detecting runtime-gated dispatch as a third state, and growing the ecosystem scan into a public dashboard.
Related project

coldpath: The Arm AI Ecosystem Ships with the Matrix Unit Off. I Wrote the Tool that Finds It, Filed the One-Line Fix Upstream (Ollama PR #17654), and Gated It in CI.

View the project