bench(ane): the Neural Engine as a third execution engine, ANE-0..ANE-4 - #312
Open
chrishayuk wants to merge 1 commit into
Open
bench(ane): the Neural Engine as a third execution engine, ANE-0..ANE-4#312chrishayuk wants to merge 1 commit into
chrishayuk wants to merge 1 commit into
Conversation
What the ANE is worth to LARQL decode, on this machine, established by
falsification rather than intuition. Harnesses, banked measurements with
provenance, and eight adjudications that each state what they do NOT
license.
## The ladder
ANE-0 Apple's 2026 Core AI stack is unavailable here (active SDK 15.5).
The ANE is reachable only through macOS 15-era Core ML, and
`computeUnits` is a PREFERENCE — a claim of "ran on ANE" without
an `MLComputePlan` behind it is not a measurement.
ANE-0b Frozen same-shape GPU control at Qwen3.8's real gate/up shape,
5120 -> 17408: 288.7 / 289.1 GB/s raw across two sessions, 0.15%
apart. Not the roofline programme's headline 367 GB/s, which came
from a different access pattern.
ANE-1 A real decode-shaped op PLACES on the ANE: 111.9 GB/s equivalent,
and the ANE's output agrees with Metal's to 1.3e-3 on the same
projection — two independent engines, same answer.
ANE-2 Placement is governed by the REDUCTION DEPTH, not by size.
Admissible k <= 16384 (hard, precision-independent); preferred
from ~4992 at f16, ~4096 at int8. Bytes do not order it:
5120->512 is 5.24 MB on ANE, 4096->8192 is 67 MB on CPU.
Batching to N=8 costs 4%. int8 halves footprint and buys ~1.15x.
ANE-3 Concurrency is BUBBLE RECOVERY. GPU+ANE together deliver
1.20-1.24x the GPU alone and cost the GPU 17-18%. Order-reversal
caught a 9.8% power-state drift that had flipped the verdict; a
pre-load ramp before every condition cut it to 0.82%.
ANE-4 A real Qwen FFN block stays 8/12 on the ANE and the heterogeneous
seam costs ~2.4%. The down_proj k-split achieves full residency
and is REJECTED: 5-8% slower and 6.2x less accurate than leaving
the tail on the CPU.
## The result that changes how placement should be chosen
Production FFN plan: ANE gate/up -> CPU tail.
Maximum accelerator residency is not the optimal physical plan. Forcing
the tail back onto the ANE buys more residency, worse latency and
substantially worse numerics at once. That is an argument for VINDEX3
owning placement by measured cost and quality, rather than treating
accelerator percentage as an objective.
A second distinction fell out of the k-split: `k <= 16384` behaves like a
hard per-operator capability, while the ~4992 lower edge is a SOFT,
graph-contextual preference — 4352-wide pieces stayed on the ANE when
their neighbours were already there. A backend model should separate
`can_run` from `cost` from `place`.
## Two corrections recorded in place rather than rewritten
- "GPU + ANE ~= 400 GB/s, the fabric ceiling" was a peak-vs-sustained
artifact: those were min-latency rates. Sustained is 233-257 + 103-107,
and the concurrent aggregate 311-323, all under ~400. The fabric was
never tested.
- The ANE accuracy gap is a FLAT ~1.5e-2 precision floor, not fp16
accumulation. Error is constant across a 3.2x range of reduction depth
(0.97x) while the CPU's grows 2.31x. The first explanation was wrong,
and it was wrong in a way that would have made the k-split look
promising.
Both are kept beside the results they correct, because the reasoning is
the part worth reading.
## Method
Every rung is banked with provenance (git SHA, hardware, OS, thermal
state before and after, process snapshot) and every runner enforces its
own protocol rather than relying on memory — refusing on battery unless
overridden with a label that says so, refusing to overwrite a banked
session. Measurements are synthetic-tensor and battery-regime throughout,
and each adjudication says so.
Three hazards were caught by controls rather than by luck: a silent
CPU-BLAS fallback that would have reported a device switch as a batching
curve; a SIGPIPE that killed a runner between writing provenance and
producing a result; and the power-state drift above.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What the Neural Engine is worth to LARQL decode, on this machine, established by falsification. Harnesses, banked measurements with provenance, and eight adjudications that each state what they do not license.
No production code paths change — this is
bench/ane/,bench/baselines/ane/, threelarql-compute-metalexamples, and a roadmap.The ladder
computeUnitsis a preference, so "ran on ANE" without anMLComputePlanis not a measurement.5120 → 17408: 288.7 / 289.1 GB/s across two sessions, 0.15% apart.k ≤ 16384; preferred from ~4992 (f16) / ~4096 (int8). Batching to N=8 costs 4%. int8 halves footprint, buys ~1.15×.down_projk-split achieves full residency and is rejected — 5–8% slower and 6.2× less accurate.The result that changes how placement should be chosen
Maximum accelerator residency is not the optimal physical plan. Forcing the tail back onto the ANE buys more residency, worse latency and substantially worse numerics simultaneously. That argues for VINDEX3 owning placement by measured cost and quality rather than treating accelerator percentage as an objective.
A second distinction fell out of the k-split:
k ≤ 16384behaves like a hard per-operator capability, while the ~4992 lower edge is a soft, graph-contextual preference — 4352-wide pieces stayed on the ANE when their neighbours were already there. A backend model should separatecan_runfromcostfromplace.Two corrections, kept beside the results they correct
Both are recorded in place rather than rewritten, because the reasoning is the part worth reading.
Method
Every rung is banked with provenance — git SHA, hardware, OS, thermal state before and after, process snapshot — and every runner enforces its own protocol rather than relying on memory: refusing on battery unless overridden with a label that says so, refusing to overwrite a banked session. Measurements are synthetic-tensor and battery-regime throughout, and each adjudication says so.
Three hazards were caught by controls rather than luck:
flop_thresholdthat would have reported a device switch as a batching curve;Follow-on
The wide-and-shallow drafter thesis this produced is being pursued separately in #311, which builds the reduced-depth execution slice it needs.