dasLLAMA: Apple +AMX tier — dasAccelerate module, BNNS-f16 lane, 3-config board scoring (both cpu board reds green) - #3562
Merged
Merged
Conversation
…ing curves) + dasprof cpu/blas patches for the ref-tree side-by-side Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLduse7eHSS1iJ2BG2P4LT
…erride (DASLLAMA_ACCEL=1) New das_accelerate C++ module (Apple-gated, -framework Accelerate): accel_sgemm_nt/accel_sgemv_n, the exact ggml-blas call shape. dasllama_math grows a float-batch override rail (fp32/bf16 batch slots only — P0 ceilings put the quant crossover out of reach on M1; raw-CPU tier bit-unchanged when unarmed). dasllama_math_accelerate driver arms it via DASLLAMA_ACCEL=1 with a min-ntok gate (default 32; DASLLAMA_ACCEL_MIN_NTOK=1 = the decode GEMV probe lane for M4/M5-SME). test_accel_backend: fp32/bf16 tolerance parity vs portable + bit-exact below-gate fall-through; feints cleanly off-Apple. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLduse7eHSS1iJ2BG2P4LT
…ch (the contention fix) 35B A/B/A caught Accelerate's internal pool starving against the spinning jobque (60 interleaved DN/router sgemm calls -> constant ~40ms/call tax, N=64 205->25 tok/s). Fix: module ctor pins VECLIB_MAXIMUM_THREADS=1 (explicit env still wins) and the driver strip-dispatches each sgemm over our own lanes (clamp(d/256, 1, 8) strips -> both AMX blocks, no foreign pool). Validated: 35B pp512 219.7 -> 227.8 (win = moe_route 125 -> 47ms, the portable-fp32-body router GEMMs; DN planes wash), E4B 209.2 -> 227.4 (strip form matches the multithreaded ceiling on the one-shot PLE sgemm). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLduse7eHSS1iJ2BG2P4LT
… diagnostic Accelerate's threading model is per-thread TLS state (thread_api.h), so the single-threaded pin must happen on each calling lane, not at module init — the ctor setenv also loses the race with Accelerate's image initializer reading VECLIB_MAXIMUM_THREADS (kept as pre-macOS-15 fallback). accel_pin_single_thread() runs lazily at the top of both externs; accel_threading_mode() exposes the calling thread's effective mode for the contention rig. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLduse7eHSS1iJ2BG2P4LT
…fault 512) Warm A/Bs: engaging tiny fp32 GEMMs (MoE routers, ~130-180 MMAC) net-loses at model level even when their bucket improves (26B -2.1%, 30B -0.9% — per-call AMX engagement tax lands outside the bucket); E4B's 14-GMAC proj is untouched by the floor. Default 512 MMAC = zero regressions across the ladder, E4B +8.7% kept. 35B's +3.7% (3 sub-floor calls/layer whose density amortizes the wake tax) is deliberately traded for the no-regression default — recoverable per box via the env/tune knob; warm-window heuristic noted in the arc ledger. Tests pin the floor to 0 (tiny shapes must engage, not fall through). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLduse7eHSS1iJ2BG2P4LT
…ll Apple-platform availability guards dasMetal-convention disable knob; the BLASSetThreading __builtin_available guard now lists iOS 18/tvOS 18/watchOS 11 explicitly — the bare '*' passed on ANY iOS version (CMake APPLE is true on the iOS CI lane, Accelerate exists there, and the call is iOS-18+). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLduse7eHSS1iJ2BG2P4LT
…terference rig Reproduces the MoE-router call pattern (small sgemm between jobque NEON bursts) without a model load; prints per-call ms + BLASGetThreading per context (healthy ~0.5ms/call, contention ~50ms). The rig that separated pool contention from the cold-dlim fault storm; reusable for the BNNS-f16 arm and any future internally-threaded-library donor. METAL=1 adds a live Metal device. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLduse7eHSS1iJ2BG2P4LT
… rig M1 Max verdicts at the E4B proj shape: dynamic two-input filters only (b_is_weights apply rc=-1); f16xf16->f32 has a proven f32 accumulator, 2316 GFLOPS internal-MT, ~12ms best pthread-strips == the internal ceiling; f32xbf16-direct works but does not strip-scale -> shipped arm converts weights bf16->f16 once and strips f16xf16. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
accel_bnns_hgemm_nt extern: dynamic BroadcastMatMul filter, f16 x f16 -> f32 with a proven f32 accumulator, n_threads=1 per lane, ~0.2us create-per-call. Driver lane: weight plane converts bf16->f16 once (exact in f16 normal range, clamped outside, pointer+fingerprint cache), activations f32->f16 per call via the KV codec convert, strip-dispatch over the jobque; DASLLAMA_ACCEL_F16_STRIPS tune knob. Falls back to the widen+sgemm path below the probe or on filter refusal. M1 warm A/B (E4B pp512, adjacent pairs): wash vs the sgemm arm (within +-1%) — ships uncrowned per never-gate; the target is M4/M5 SME where f16 is 2x f32. Contention rig gains a BNNS strips phase (healthy ~21ms avg at the proj shape, no jobque starve). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-backend lcpp_bench --accel leg (flavor 'accel', the +AMX tier row); gen_bench_records --legs neon|amx|metal with each ratio's das cell and ref run adjacent: generic neon-64 = das cpu vs clean-cpu (kernel vs kernel), cpu + AMX = das --accel vs stock -ngl 0 -nopo 1 (-nopo keeps lcpp's Metal op-offload from serving 'CPU' rows on the GPU — the 26B/E4B stock-cpu reds were this pairing error), as shipped = das metal vs stock -ngl 99. Site ratio derivation pairs by config instead of best-per-backend. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lls green das cpu accel rows (all 8 models) + stock refs re-run -ngl 0 -nopo 1. cpu + AMX column: E4B 1.08x (was 0.958 red — the PLE-proj accelerate win), 26B 1.24x (was 0.861 red — the old pairing compared pure CPU against hidden Metal op-offload), rest 1.03-1.75x; accel == tuned parity everywhere the work floor declines (as designed). 26B neon pair re-measured: das 145.5 +- 3.3 vs clean 121.6 = 1.20x (the old 134.4 +- 8.2 was a churn-polluted cell). gen_bench_records grows a self-healing variance tripwire: the FIRST das process on a cold/churned .dlim map warms across its reps (35B: 150->198 monotone within one cell while the next process reads a flat 212 +- 1.7) — a >3% cv das cell re-runs once warm and keeps the retry. Caught and healed 30B and both 26B cells. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…as silently skipped 38a1077's row guards ("NOT DAS_X_DISABLED") reach if() as ONE argument with an embedded space, which if() reads as an undefined variable name — falsey — so if(NOT (guard)) killed emission for EVERY guarded reg row (pugixml, dashv, dassqlite, strudel, live_host, minfft test stubs all dropped from test_aot; the full-AOT sweep fails 50101 on all of them after any fresh configure; latent locally until a reconfigure regenerated the graph). Split the guard into real tokens before evaluating. Repro + fix verified in isolation on CMake 3.29.5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d re-check + announced --no-verify Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds an Apple-specific “+AMX/SME” CPU acceleration tier for dasLLAMA via a new das_accelerate dynamic module (Accelerate BLAS + optional BNNS f16 matmul lane), and updates the public benchmark tooling/site to score dasLLAMA vs llama.cpp using matched configuration pairs (generic CPU vs clean-cpu, accel vs stock -ngl 0 -nopo 1, Metal vs -ngl 99). It also fixes a recently-latent AOT CMake guard parsing issue that caused guarded irregular AOT rows to stop emitting.
Changes:
- Add
modules/dasAccelerate(Apple-only) and wire a float-plane batch override (set_float_batch_override) to support an opt-in Accelerate-backed CPU tier for dasLLAMA. - Update benchmark record generation and website presentation to use matched-pair denominators (and record llama.cpp CPU refs with
-nopo 1to avoid GPU op-offload at-ngl 0). - Fix
tests/aot/CMakeLists.txtirregular-table guard evaluation by tokenizing guard expressions beforeif()evaluation.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/aot/CMakeLists.txt | Fixes irregular AOT row guard evaluation by splitting the guard string into if() tokens. |
| skills/make_pr.md | Updates pre-push / preflight process guidance (single full-preflight per PR). |
| skills/babysit.md | Aligns post-review workflow guidance with the “one full-preflight per PR” policy. |
| site/files/dasllama/bench_records.json | Updates/extends published bench records (adds accel rows; records llama.cpp CPU refs with -nopo 1). |
| site/files/dasllama.js | Changes ratio computation to matched-pair denom selection per das configuration. |
| site/dasllama.html | Updates narrative to explain matched-pair scoring and -nopo rationale. |
| modules/dasLLAMA/tests/test_accel_backend.das | Adds tests for fp32/bf16 override parity + BNNS f16 lane behavior and below-gate fall-through. |
| modules/dasLLAMA/PUBLIC_BENCH_PLAN.md | Updates public methodology docs for accel flavor and matched-pair ratios. |
| modules/dasLLAMA/performance/records/m1.json | Updates/extends per-box official records (adds accel rows; updates CPU refs with -nopo 1). |
| modules/dasLLAMA/performance/profile_common.das | Documents new das run flavor accel in the bench run schema. |
| modules/dasLLAMA/performance/gen_bench_records.das | Adds --legs pairing, adjacent ref runs per leg, and high-variance auto rerun logic. |
| modules/dasLLAMA/harness/dasprof_cpu.patch | Adds reproducible ggml CPU-side profiling patch for section timing/chunk counts. |
| modules/dasLLAMA/harness/dasprof_blas.patch | Adds reproducible ggml BLAS-side profiling patch for dequant/sgemm timing split. |
| modules/dasLLAMA/harness/bnns_hgemm_probe.c | Adds BNNS dtype/accumulator/profiling probe used to justify the BNNS f16 lane design. |
| modules/dasLLAMA/harness/accel_sgemm_bench.c | Adds Accelerate SGEMM/SGEMV ceiling benchmark utility for Apple boxes. |
| modules/dasLLAMA/harness/accel_contention_probe.das | Adds contention probe for Accelerate/BNNS under jobque strip-dispatch. |
| modules/dasLLAMA/dasllama/dasllama_math.das | Introduces the float-plane batch override rail (set_float_batch_override) and hooks it into batch matmuls. |
| modules/dasLLAMA/dasllama/dasllama_math_accelerate.das | Implements Accelerate-backed fp32 and bf16 batch overrides (+ optional BNNS f16 lane) and env-controlled arming. |
| modules/dasLLAMA/dasllama/dasllama_common.das | Wires the accelerate driver into the dasLLAMA umbrella via require ?das_accelerate. |
| modules/dasLLAMA/benchmarks/lcpp_bench.das | Adds --accel flag to produce das “accel” rows for bench record generation. |
| modules/dasLLAMA/.das_module | Adds dasllama_math_accelerate to the module’s export path list. |
| modules/dasAccelerate/src/dasAccelerate.cpp | Adds Apple Accelerate/BNNS C++ shim exports (accel_sgemm_nt, accel_sgemv_n, accel_bnns_hgemm_nt, diagnostics). |
| modules/dasAccelerate/CMakeLists.txt | Adds Apple-only build wiring for the new dynamic module and Accelerate framework link. |
| modules/dasAccelerate/.das_module | Registers the dynamic module at runtime for DLL builds (silent skip off-Apple). |
| CMakeLists.txt | Adds DAS_ACCELERATE_DISABLED option to control building the new module. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Lane jobs clone captures — a captured scalar write is lost (probe: scalar_hit=0, ptr_hit=1), so a refused filter could return true without falling back. Copilot catch on #3562. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…very driver symbol CI's linux lint compiles each changed file, and das_accelerate does not exist there: the helper bodies' unconditional driver calls failed the whole file (the [test] feint only covered the test arm). Shims route every driver reference through builtin_module_exists; verified by linting with the shared_module hidden — the exact linux condition — 0 errors both ways, 5/5 on-Apple. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
borisbat
added a commit
that referenced
this pull request
Jul 25, 2026
…ry/ doc archiving (#3563) * release: bump version to 0.6.4 + 0.6.4 changelist Version 0.6.3 -> 0.6.4 across platform.h, CMake, Sphinx conf, the get_das_version doc example, and install/README. CHANGELIST.md gains the 0.6.4 (July 2026) section covering #3272-#3562. version_update.md skill corrected: conf.py carries the full X.Y.Z in both fields (de-facto since 0.6.3). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * install: sync SDK CLAUDE.md to 0.6.4, ship dashv + find_dupe skills, scrub personal paths install/CLAUDE.md picks up the 0.6.4-era language content (gen2 default parser, bare named calls, with (module), cast parens, 16/8-bit lattice, distinct types, addr<T?> sugar, delete-on-pointer-containers, options stack, reserved words, the require hyphen rule, STYLE034/035 rows) plus dashv/find_dupe skill rows; install/skills.list ships both skills. Skills scrub: no personal names or machine-local paths in skill content (external_module_debugging examples use a generic package root now). Top-level CLAUDE.md: the named-arguments bullet documents the bare foo(pos, name = value) form (probe-verified), and the stale pre-#3420 reinterpret operand-swallow gotcha is removed (probe-verified: the cast is self-delimiting now). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * history/: archive completed-arc docs + doc_archiving skill New skills/doc_archiving.md defines the archive-vs-stay test and the process: git mv under history/<area>/, update path-qualified references, add a note in the area's living index doc, and record every doc in the Archive log at the bottom of history/README.md (the findability ledger). Swept per the skill: the root rework docs (FIXED_ARRAY_REWORK, ANNOTATION_INFO_REWORK, QUOTE_LOWERING, COVERAGE_GAP), the dasSQLITE API rework corpus + tutorial-mockup design artifacts, the dasSpirv PHASE6-9 working plans, closed dasLLAMA docs (avx_kernel_matrix, epyc9654_measurements, model_expansion_plan), the benchmarks/sql linq_fold plans/audits, example plans (sfx_lab, scalar_packing, sequence, Cadmus), and the daScriptTest migration checklist. ~33 path-qualified references updated across daslib, tests, tutorials, RST, and skills; living contracts (PROVIDER_CONTRACT, the MASTERPLANs, cited dasLLAMA specs, results.md ledgers) stay put and carry archive notes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * tests/linq: clear pre-existing lint in files touched by the archive sweep The archive sweep's comment-path updates put 7 linq_fold test files into CI's changed-files lint set, surfacing 20 pre-existing warnings: unused eid block arguments on create_entities callbacks (now [unused_argument(eid)] per the in-file precedent — the name is decs-bound and cannot be underscore-renamed) and unused require math / require strings lines (dropped). tests/linq suite: 2007/2007 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * release: finish 0.6.4 docs, site, and archive ledger Update the two manual titles and all live site version chips to 0.6.4, correct the version-update instructions to match established full-X.Y.Z practice, and clarify the Sphinx version comment. Expand the archive ledger to one exact old-to-new entry per moved document and repair local links in the archived docs. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.
The Apple "+AMX" CPU tier for dasLLAMA, and the board scoring that measures it honestly. Closes out the two cpu-vs-stock board reds (E4B 0.958, 26B 0.861) — both green under matched-pair scoring, with every M1 cpu cell now ≥ 1.0 in both cpu configs.
Three-tier product framing
Apple inference now ships three gated configurations, each with a real use:
DASLLAMA_ACCEL=1) — Accelerate BLAS serving float-plane batch GEMMs, for when the game owns the GPU;modules/dasAccelerate (new)
Apple-only dyn module over Accelerate:
accel_sgemm_nt/accel_sgemv_n(ggml-blas call shapes),accel_bnns_hgemm_nt(dynamic BNNS BroadcastMatMul filter, f16×f16→f32 with a probe-proven f32 accumulator, ~0.2µs create-per-call),accel_threading_modediagnostics. Accelerate's internal pool starves against the spinning jobque (~40ms/call flat), and its threading mode is per-thread TLS state — so every calling lane pinsBLASSetThreading(SINGLE)(BNNS pins vian_threads=1) and the das side strip-dispatches over the jobque instead. Default-ON on Apple like dasMetal (DAS_ACCELERATE_DISABLEDopt-out), so it rides existing SDK packaging.Driver (
dasllama_math_accelerate.das) + the float-batch override railset_float_batch_overrideindasllama_math— fp32/bf16 batch slots only; the raw-CPU tier is bit-unchanged when unarmed. The driver arms it fromDASLLAMA_ACCEL=1:DASLLAMA_ACCEL_MIN_MMAC(default 512) — tiny GEMMs (MoE routers) net-lose on the AMX engagement tax and fall through to NEON;DASLLAMA_ACCEL_F16=1, default OFF): weight plane converts bf16→f16 once (exact for every bf16 value in f16 normal range; ±65504 clamp; pointer+sampled-fingerprint cache guards mmap address reuse), activations convert per call via the f16 KV-codec kernel, strips over the jobque (DASLLAMA_ACCEL_F16_STRIPSknob). On M1 it is a wash vs the sgemm arm (within ±1% across adjacent-pair reps) and ships uncrowned — the target is M4/M5 SME where f16 is 2× f32 throughput. Falls back to widen+sgemm on probe/filter refusal.In-model, the arm's one real M1 engagement is the E4B/E2B bf16
per_layer_model_proj(14.1 GMAC/pass): clean-CPU NEON has no bf16 ISA on M1, stock llama.cpp serves it on AMX — this arm answers with the same silicon (E4B pp512 +13% over raw CPU, the board red closed).Board scoring: three matched pairs, never best-on-backend
llama-benchat-ngl 0still op-offloads big-batch host-weight ops to Metal on a stock build (26B: 194MB MTL0 compute buffer, +33% pp) — pairing das-pure-CPU against that row was a category error, and it was the whole 26B "red". The board now derives each das ratio against its named llama.cpp sibling:-ngl 0(kernel vs kernel)--accel-ngl 0 -nopo 1(Accelerate both sides, no GPU;-nopo≡ no-Metal build, window-proven)-ngl 99Rails:
lcpp_bench --accel(records flavoraccel),gen_bench_records --legs neon|amx|metalwith each pair's das cell and ref run adjacent, site derivation + prose updated,PUBLIC_BENCH_PLAN.mdupdated.M1 records re-sweep (pp512, das/lcpp)
tg128 is untouched by the arm (override gates at ntok ≥ 32) — accel ≡ tuned decode everywhere. The honest pairing surfaces two pre-existing cpu-decode gaps (gpt-oss 0.97, 26B 0.96–0.97 — the old derivation hid the first behind lcpp's own op-offload decode loss); those are follow-up work, tracked outside this PR.
Measurement integrity: the sweep exposed that the FIRST das process on a cold/churned
.dlimmap warms across its reps (35B: 150→198 monotone within one cell; the next process reads a flat 212 ± 1.7) — one in-process warmup pass cannot absorb a 19GB map fighting page-cache eviction.gen_bench_recordsnow carries a self-healing variance tripwire: any das cell over 3% cv re-runs once warm and keeps the retry (it caught and healed three cells during this sweep, including a stale pre-existing 26B baseline). The product-level fix candidate (eager-prefault option in the dlim mapper) is deliberately not part of this PR.Harness / instruments (committed for reproducibility)
bnns_hgemm_probe.c(BNNS dtype matrix, accumulator-width proof, strips scaling — all M1 verdicts in the header),accel_sgemm_bench.c(Accelerate ceiling curves),accel_contention_probe.das(+ BNNS phase),dasprof_cpu/blas.patch(ref-tree op profiling).Rider fix: tests/aot irregular-table guards were dead (master, hours old)
The first full-preflight over today's
38a1077a2(per-suite AOT loop collapse) failed 50101 on every dasPUGIXML/dasHV/dasSQLITE test: the table's row guards (NOT DAS_X_DISABLED) reachif()as a single space-embedded argument, which CMake reads as an undefined variable name — so every guarded reg row silently stopped emitting its AOT stubs (pugixml/dashv/dassqlite/strudel/live_host/minfft). Latent until a fresh configure regenerates the graph — which also means the nightly full-AOT sweep hits it. Fixed by splitting the guard into real tokens before evaluation; repro + fix verified in isolation on CMake 3.29.5, all six areas emit edges again.Testing
modules/dasLLAMA/tests/test_accel_backend.das5/5: fp32/bf16 tolerance parity, BNNS f16 lane parity, below-gate bit-exact fall-through; feints cleanly off-Apple. The f16 flag is pinned off in setup so env arming cannot leak into the sgemm arms.--no-verifyper policy, CI validates the tip.🤖 Generated with Claude Code