opus colibri scout: what JustVugg/colibri has that vendor/strata/metal/ doesn't #
clone: https://github.com/JustVugg/colibri @ depth 1 in /tmp/colibri (340k lines; the Apple GPU code is c/backend_metal.mm,
2,367 lines, kernels inline as one MSL string at lines 27–691). our side: vendor/strata/metal/ at the working tree as of this run.
probes (4 small Metal/ObjC++ programs, ~1 min of GPU in total, run while no strata process was up; nothing in vendor/ edited,
:8080 not touched): /tmp/colibri_probe/{bench,benchA,benchA2,benchC,benchD}.mm, raw numbers in /tmp/colibri_probe/results.txt.
0. tl;dr #
colibri is a CPU engine with an opt-in Metal offload layer, built around models far bigger than RAM (GLM-5.2 744B, Kimi K3,
DeepSeek V4.x). its best Apple number is 2.24 tok/s on an M5 Max (docs/METAL-M5MAX-PERF-REPORT.md). for Qwen3.8 Flash Next
(our model) colibri has no Metal path at all: c/qwen38.c only wires the Vulkan tier (qwen38.c:2220-2409, q38_vk_*), and
its docs measure 1.1–4.0 tok/s on x86 CPUs (docs/qwen38.md, "Memory and speed" and the int4 table). strata does ~21–22 tok/s
on the 24 GB M5 Air with all 512 experts. so the honest answer is: at the kernel, runtime and SSD-streaming level there is
nothing in colibri that's ahead of us, and a few of the things the brief flagged as suspects (hazard tracking, residency sets,
passive waits, entropy coding) i measured and they don't pay here.
what is worth taking is host-side policy, ranked:
| # | steal | colibri source | strata today | expected on the M5 Air | effort |
|---|---|---|---|---|---|
| 1 | online cost-model draft depth (SpecGate: pick k maximizing E(k)/cost(k) from measured per-position acceptance and measured T(n), k=0 allowed) |
c/spec_draft.h:14-160 |
fixed --spec 3 --spec-min-p 0.35 --suffix-draft 3 (strata_window_cli.cpp:709-780, 852-853; config models/K512_iq2_xs/strata-k512-iq2xs.json) |
workload-dependent: 0 on high-acceptance text, ~5–10 % on stretches where drafts mostly fail (a T=3/4 window that keeps 1 token is a pure loss under streaming); also the right place to stop drafting during prompt-heavy / tool turns | ~120 lines host-only, no kernel, exactness unchanged |
| 2 | max-rank window on MoCE substitution (CACHE_ROUTE's ROUTE_M: a resident may only replace a slot if it is inside the true top-M) |
docs/CACHE_ROUTE.md, ROUTE_J/ROUTE_M |
router.metal:43-111 scales every resident's prob by λ from rank r_prot on, no rank cap |
quality guard, ~0 ms: bounds the worst-case substitution (a rank-40 resident can win today if within the 0.85-logit bonus). measure routed relMSE vs misses with the existing replay (/tmp/opus_metal_probes) at M = 16/24/32 |
~20 lines in router_route |
| 3 | learned expert-usage history that survives restarts (.coli_usage, decayed counts appended per turn; startup pin = history; optional between-turn re-pin) |
c/route_trace.h:1-60, c/colibri.c:13216-13255 (PIN=auto), colibri.c:9729-9739 (REPIN) |
static calibration profile data/expert-profile.bin (stream.cpp:440-466), fill 4,129 slots from its top in 1.6–1.9 s (serve log) |
first-turn hit rate after a restart + prompt-hot protection (hold_region) aligned to the persona's real traffic. steady state is LRU-driven and won't move |
~80 lines in ExpertStream (dump LRU order + counts at exit/idle, blend at make_arena) |
| 4 | PLE rows issued early, collected later (the row ids are a pure function of the tokens, so the reads can start before anything else and be collected at the layer that needs them) | c/qwen38_core.h:2060-2110 (q38_ple_prefetch) |
decode window gathers synchronously before the CB (window.cpp:1480-1491), and the PLE GEMVs sit before layer 0 (window.cpp:1546-1551). the async API exists (ngram.cpp:398-407 prefetch_rows, 507-538 fast path) and the CUDA/core path uses it (src/core/mtp.cpp:1581, verify.cpp:2264), the metal window doesn't |
small: "host before" is 0.225 ms/window in total (audits/opus_strata_metal_opts_v3.md:111), so ≤ ~0.2 ms (≤ 0.5 %). worth it only bundled with #1 (prefetch the rows of the MTP drafts right when mtp_draft returns) |
~30 lines |
| 5 | cheap diagnostics colibri ships and we don't: a grid-size guard (backend_metal.mm:1653-1680, they saw silent corruption past ~5.4e7 threadgroups) and a GPU keep-alive experiment (backend_metal.mm:1060-1085, COLI_METAL_SPIN) for the first window after idle |
see left | require_fit checks threads per TG only (mtl_rt.mm:159-169); warm() exists (mtl_rt.mm:336-339) but nothing runs between requests |
0 ms steady state; the keep-alive could matter for TTFT after the server sat idle (DVFS ramp), unmeasured | trivial |
everything else colibri does on Metal we either already do better or i measured to be worthless here (§3, §4).
1. architecture side by side #
colibri (c/backend_metal.mm) |
strata (vendor/strata/metal/) |
|
|---|---|---|
| what runs on the GPU | per-call offload: coli_metal_matmul (1118-1167), coli_metal_gemm (1637-1687), fused MLA layer for S≤4 (coli_metal_layer_decode, 1532-1632), batched routed-expert SwiGLU (moe_submit_impl, 1738-1840). everything else (routing bookkeeping, scatter-add, GDN/KDA prep, sampling) on the CPU with OpenMP |
the whole decode window — 48 layers, router, MoE, GDN, QSA, HC/PLE, head, argmax — one command buffer (window.cpp:1460-1608, cached per window shape 1506-1532) |
| host↔GPU data | newBufferWithBytes/memcpy of activations in and out of every call (1151-1164, 1511, 1564, 1623-1629, 1788); scatter-add of expert outputs on the CPU (moe_finish 1854-1856) |
activations never leave the GPU inside a window; expert outputs combined on the GPU (moe_mm.metal:269 mm_combine, fused variants in ple.metal:174-183) |
| command buffers | one CB per layer/op, commit + waitUntilCompleted each (e.g. 1163, 1517, 1619, 1682); docs call Metal's submit "~5 ms" (docs/metal.md) |
one CB per window; inside it 48 segments with a MTLSharedEvent handshake per layer (mtl_rt.mm:273-316), shared-expert work placed between signal and wait (sigs, window.cpp:1612) |
| encoder | default serial computeCommandEncoder; explicit memoryBarrierWithScope:Buffers inside the attention chain (1413, 1447, 1571, 1813-1834) |
MTLDispatchTypeConcurrent + explicit barriers only where a dispatch depends on the previous (mtl_rt.mm:252-255, 283-288), commandBufferWithUnretainedReferences (230, 251, 279) |
| buffers | MTLResourceStorageModeShared; COLI_METAL_UNTRACKED=1 adds HazardTrackingModeUntracked (753, 928-929), default off, never measured in-tree |
Shared, default tracking (mtl_rt.mm:171-198) |
| residency | useResource: per slab per CB (1380, 1513-1514, 1567-1570, 1801); COLI_METAL_RESSET=1 puts slabs in a queue-attached MTLResidencySet (776-862, 995-1017), default off, its own doc says "unverified without a run" (docs/experiments/e5-metal-residency-set.md, table row "Known regression") |
not needed: weights are 1 zero-copy wrap of the file map (stream.cpp:206-238) + an arena of ≤ 4 buffers (stream.h:58-59; serve log: "1 buffers"), bound with setBuffer per dispatch, so Metal makes them resident implicitly |
| expert addressing | per-dispatch tables of gpuAddress per expert built on the CPU and uploaded with newBufferWithBytes (1771-1787), kernel dereferences waddr[e] (267-279) |
smap (48 × 512 uint32: chunk << 28 | 16 KiB page) written by the I/O threads, read by the kernels (moe.metal:52-56, function constant 20); resident-first split (moe.metal:66-123, constant 26) |
| SSD reads | pread (+ O_DIRECT/F_NOCACHE twins, st.h:276, compat.h:55-61), posix_fadvise(WILLNEED) → F_RDADVISE on macOS (compat.h:30-48) as a read-ahead hint (qwen38_core.h:1523-1549); OpenMP loader pool (K3_PIPE, PIPE_WORKERS); io_uring on Linux (uring.h) |
F_NOCACHE + F_RDAHEAD 0 (stream.cpp:60-64, 117-119), one request per expert from the contiguous .experts copy (stream.cpp:79-128, 341-360), 8 I/O threads with 2 reserved for urgent misses (stream.cpp:156-158, 374-402), pieces of one miss on several threads (read_part, 338-372) |
| cache | per-layer LRU + learned pin from .coli_usage, live REPIN, CACHE_ROUTE |
size-classed LRU arena (stream.cpp:129-155, 282-336), profile fill, MoCE (λ=0.85, protect 3), next-layer read-ahead from W(l+1)·XF(l) (stream.cpp:656-716), deterministic limbo retirement (stream.h:105-109) |
| quant decode | fmt 1/2/3/4/5/6/8: int8, int4 (per-row / grouped), int2, bf16, E8/IQ3 lattice with FWHT-rotated activations (moe_fwht 345-364), fp8 e4m3 by bit tricks (126-144); fp32 activations, scalar dot(float4) |
the ggml/IQ families + Q2_0 + GSQ-RCO packs with templated decoders (quant.metal:24-120), constant-memory vs threadgroup grid tables per format (QUANT_CTAB, window.cpp:1354-1355), matmul2d/MPP tiles for prompts (mm_bench.metal:2-18, serve log "matmul2d layout K4") |
| attention / recurrent | MLA absorption with materialized scores (a_score/a_smax/a_clat, 421-445); KDA recurrence one thread per (h,i) re-reading S from device twice (652-690) |
QSA flash/top-k/merge (qsa.metal:93-399), GDN step kernels (gdn.metal:36), all fused into the window |
| speculative | MTP up to 3 + prompt lookup up to 5, depth chosen online by SpecGate (spec_draft.h); state snapshot per verify row for GDN/PLE rewind (docs/speculative.md) |
MTP (one CB per draft round, window.cpp:2805-2808) + suffix drafter (strata/spec/suffix_drafter.hpp), typical / exact sampled acceptance (strata_window_cli.cpp:383-415), fixed depth |
2. what i measured (Apple M5, 10-core GPU, macOS 27.0, no strata running) #
2.1 hazard tracking: tracked vs MTLHazardTrackingModeUntracked — no difference #
benchA.mm/benchA2.mm: 1,600 dispatches per CB, 7 buffer bindings + 16 B of setBytes each (≈ a decode window's shape),
commandBufferWithUnretainedReferences, 24 × 4 MiB buffers rotated so every dispatch reads the previous one's output. warm, median of 25:
| dispatch | encoder | barrier | tracked: encode / GPU | untracked: encode / GPU |
|---|---|---|---|---|
| 1 TG, trivial | concurrent | every dispatch | 0.601 / 1.548 ms | 0.596 / 1.575 ms |
| 1 TG, trivial | concurrent | none | 0.320 / 0.110 | 0.321 / 0.109 |
| 1 TG, trivial | serial | (implicit) | 0.309 / 1.412 | 0.307 / 1.411 |
| 64 TGs, trivial | concurrent | every dispatch | 0.609 / 2.147 | 0.605 / 2.137 |
| 64 TGs, ~20 µs ALU | concurrent | every dispatch | 0.595 / 99.40 | 0.595 / 99.42 |
- tracked = untracked within noise in every arm. the first run of
bench.mmshowed tracked 3.6 ms vs untracked 1.7 ms GPU, but that was tracked-first on a cold GPU; with a warm-up and the order swapped the gap is gone (benchA2). don't flipMTLResourceHazardTrackingModeUntrackedon the strength of that kind of number. - with untracked buffers the shared-expert encoder that runs between signal and wait (
mtl_rt.mm:292-295) would also need anMTLFence, since the event wait doesn't order prior encoders. so it's risk for ~0 gain. colibri never measured its own switch either. - the number that is real: a buffer barrier costs ~0.9 µs of GPU per dispatch (1.55 ms with vs 0.11 ms without for 1,600 tiny dispatches). that's the existing barrier-pruning line of work (audits/argon_prefill_barriers.md did it for prefill), not a colibri idea.
- encode is 0.6 ms for 1,600 dispatches with barriers, matching the ~0.96 ms "encode/submit/wait" per window in
opus_strata_metal_opts_v3.md:111. replaying a cached window as an ICB (
mtl_rt.mm:341-388already hasrun_icbfor benchmarks) could save ~0.4 ms/window. adjacent, not from colibri.
2.2 the layer handshake: colibri's "passive wait" lesson does not transfer #
bench.mm B: 48 segments of a 48 MB streaming kernel, each followed by signal + wait like run_graph_seg, host releases immediately. median of 20:
| host wait | window wall | CB GPU | waiter CPU |
|---|---|---|---|
| no handshake (one encoder) | 18.44 ms | 18.15 | 0.06 |
spin on signaledValue (strata, mtl_rt.mm:301) |
21.24 | 21.03 | 21.2 |
spin + __builtin_arm_yield |
21.30 | 21.11 | 21.3 |
spin + std::this_thread::yield |
20.88 | 20.67 | 20.5 |
waitUntilSignaledValue:timeoutMS: |
27.52 | 27.24 | 0.59 |
notifyListener + condvar |
27.20 | 26.96 | 0.59 |
- spin costs ~60 µs per handshake (same as opus_strata_metal_opts_v1 §2.3's 57–73 µs); either passive wait costs ~190 µs, i.e.
+6.2 ms per token on 48 layers. keep the spin. (the
this_thread::yieldvariant is ~0.4 ms/window faster in two runs; within noise, but free to try on the real engine.) - the colibri report's trap (OMP hot-team active spin on 18 cores tripled attention kernel time on the M5 Max,
docs/METAL-M5MAX-PERF-REPORT.md, rows A→B) is about many spinning cores sharing the SoC power budget.benchC.mm: GPU streaming bandwidth with N CPU threads spinning, 160 windows each, twice: 0 → 132.4/131.3 GB/s, 1 → 132.4/131.2, 4 → 132.0/130.9, 10 → 129.7/129.6. one spinning core is free on the Air (in 6 s bursts; a 10-minute fanless thermal test is still open), 10 cost 2 %. the takeaway for us: nothing else may spin while a window runs. today that holds: I/O threads block inpread/condvars (stream.cpp:381), andgpu/runtime.cpp:182yields. - a sleep-until-predicted-then-spin hybrid (
benchD.mm) gave nonsense timings and i ran out of budget to debug it; not a result either way.
2.3 entropy coding the experts (colibri's int4-rans256-g0): doesn't apply to IQ2_XS #
colibri entropy-codes int4 expert nibbles losslessly, ratio ~0.76 (docs/int4-rans256-g0.md), because a symmetric int4 quantizer's
codes are far from uniform. on our K512-iq2_xs-metal.gguf.experts, 24 random 1 MiB pages: byte order-0 entropy 7.912 / 8 bits
(0.989), u16 entropy 15.77 / 16 (0.986), zlib-9 0.973, lzma-6 0.975. IQ2_XS grid indices + sign bits are already ~uniform, so the
most a static rANS could save is 1–2.7 % of the ~5 ms SSD wait (≤ 0.1 ms) at a real CPU decode cost. dead end.
3. the stealable items in detail #
3.1 SpecGate: online cost-model draft depth (rank 1) #
colibri (c/spec_draft.h): per draft source it keeps a_j = P(draft j accepted | drafts before it accepted) as an EMA with memory
16 verifies (spec_gate_result, 107-117), the measured wall time T(n) of an n-row forward as an EMA with memory 8 (spec_gate_forward,
85-96), a fitted ρ for row counts not seen yet, and the per-extra-draft cost d of the drafter (spec_gate_draft_cost, 99-102). each
verify picks k = argmax over 0..maxk of
value(k) = E(k) / cost(k), E(k) = 1 + a1 + a1·a2 + … + a1…ak, cost(k) = (T(k+1) + (k-1)·d) / T(1)
and drafts only if value > 1 (spec_gate_value/spec_gate_pick, 129-160). it probes one deeper every 16 picks (every 4 while that
row count has < 3 timing samples) so estimates can recover, and forces a plain step after 64 drafted forwards so T(1) stays measured.
tokens never change: it only decides how many rows the verify carries.
strata: the window size is fixed by flags. mtp_draft stops a draft chain when the drafter's prob < spec_min_p (0.35 in the
served config), suffix drafts fill up to max(suffix_draft, nd) (strata_window_cli.cpp:720-733), and nothing looks at what a
T=2/3/4 window actually cost. under SSD streaming that cost is not 1 + small: each extra row brings its own top-10 per layer, ~+60 %
experts moved for T=2 (opus_strata_metal_opts_v1 §0 #7), and sol_spec_mtp_decode.md:116-118 works out that a T=4 window only breaks
even if it finishes under ~2.5× T(1) at 0.7 acceptance. the true ratio depends on the arena state and the text, so it should be
measured online, which is exactly what the gate does.
worked example (illustrative numbers, not measured): T(1) = 46 ms, T(3) = 70 ms (1.52×), MTP draft 2 ms/step.
- code/repetitive text, a1 = 0.8, a2 = 0.7: E(2) = 2.36, cost 1.57 → value 1.50 (draft 2, +50 %).
- persona prose at temp 0.6, a1 = 0.35, a2 = 0.3: E(2) = 1.455, cost 1.57 → 0.93 < 1; E(1) = 1.35 vs cost(1) = T(2)/T(1) ≈ 1.3
→ 1.04, basically break-even. the fixed
--spec 3keeps paying T(3) or T(4) here, ~7 % slower than plain decoding.
sketch for strata (host only, strata_window_cli.cpp + a small header next to suffix_window.hpp):
struct SpecGate { double acc[2][8], acc_w[2][8], t[10], t_w[10], rho, rho_w, d[2], d_w[2]; int picks[2], since_plain; }with colibri's EMA update; source 0 = MTP, 1 = suffix.- after each
window_run(w, 1 + nd, …)(line 735):spec_gate_forward(g, 1 + nd, wall_ms). use the window's own wall time (it already exists:win_ms_[t], window.cpp:456-457), so SSD waits and the larger expert union are inside the cost. - after
keepis known (745-747):spec_gate_result(g, src, judged = min(nd, keep), accepted = keep - 1), split by source when MTP and suffix drafts are concatenated (input[1..n_mtp] vs the rest). - before building
input(720-733):k = spec_gate_pick(g, MTP, n_mtp_drafts), thennd = min(k, max_d). strata can do better than colibri here: use the drafter's ownmtp_probs[i]as a1..ak for this verify (calibrated per token, already returned bymtp_draft), and keep the EMA only as a calibration factor between the drafter's prob and real acceptance (acc ≈ c_j · mtp_probs[j]). thenspec_min_pbecomes a derived quantity instead of a hand-set 0.35. - pass
spec - 1drafts tomtp_draftas today; the gate trims.STRATA_SPEC_GATE=0restores the fixed behaviour. - report line like colibri's
spec_gate_describe(162-180): T(1), T(n)/T(1), acceptance by position. it's free telemetry we lack.
exactness: unchanged by construction (it only shortens the drafted tail). falsifier for the A/B: tok/s on the three domains
(ops_tools, chat_json, persona prose) at temp 0.6 with --spec 3; gate on must be ≥ fixed on all three, and the prose domain is
where it should win.
3.2 max-rank window on MoCE (rank 2) #
colibri's cache-aware routing (docs/CACHE_ROUTE.md, after arXiv:2412.00099) keeps the true top-J and fills the rest preferring
residents that still rank inside the true top-M (ROUTE_M=12 default), with agreement/KL telemetry (route_agree, route_kl).
strata's MoCE (router.metal:43-111) multiplies every resident's prob by moce_scale from rank r_prot on, with no rank bound. the
v1 audit found the bonus is large next to the router's spread (median z1 − z3 = 0.35 logits vs bonus 0.85), so deep-ranked residents
do get pulled in. a cap is cheap: at rank == r_prot, before scaling, find the M-th largest unscaled prob (M extra
simd_shuffle_xor argmax rounds over the 16 values per lane, or one threshold pass), and scale only residents with v[i] >= thr.
expected effect: lower routed relMSE at a small miss increase. it's a quality knob, so it needs the replay harness numbers (misses/tok
and relMSE at M = 16/24/32 vs no cap) before anyone ships it; ahead()'s eff_lg (stream.cpp:666-668) would get the same cap so
the read-ahead stays consistent with the router.
3.3 a usage history that learns across sessions (rank 3) #
colibri appends per-turn expert counts to <model>/.coli_usage (route_trace.h:19-60 format), uses it as the startup pin set when
PIN=auto (colibri.c:13216-13229), and can re-pin between turns from a decaying map (colibri.c:9729-9739). strata's arena fill
reads only the static calibration ranking (stream.cpp:440-466) and marks the top of it prompt-hot (stream.cpp:601-612). sketch:
- at shutdown and every N windows while idle, write
<gguf>.experts.usage: per (layer, expert) a decayed hit count (bump inat_layerwherewhere_is consulted, stream.cpp:798-826), plus the LRU order. make_arena: rank = sort byα·usage + (1−α)·profile_rank_score(α≈0.7 once the history has ≥ 1k tokens), same stepped fill.- keep the exact-answer property: residency doesn't change math without MoCE, and with MoCE it only changes which resident wins, as today's LRU already does. but it does change routing under MoCE, so the A/B should be after a restart, first 512 tokens, hit rate and tok/s.
3.4 PLE rows early (rank 4) #
colibri computes all PLE row ids at forward start and issues the reads in parallel, collecting at the PLE layer
(qwen38_core.h:2060-2110; its comment: "Riordino puro: stessi byte, letti prima"). strata reads them synchronously in
Window::run before the CB starts (window.cpp:1480-1491), the GEMVs on emb_ sit at the top of the window (1546-1551), and the
metal path never calls the async PleTable::prefetch_rows the core path uses (src/core/mtp.cpp:1581). the cheapest version: when
mtp_draft returns drafts (strata_window_cli.cpp:770), compute the next window's row ids for [cur_tok, drafts…] and call
prefetch_rows. gather_batch's exact-match fast path (ngram.cpp:513-537) then collects the in-flight tickets without
re-reading. bounded by the 0.225 ms "host before", so it's a small win. pair it with 3.1.
3.5 two cheap guards (rank 5) #
- grid-size check. colibri found that M-series silently skipped threadgroups past a grid of ~5.4e7 (clean at 3.1e7), leaving stale
output, and now chunks every GEMM to ≤ 2^25 elements (
backend_metal.mm:1653-1680). their kernel indexes rows with int, so it may partly be their own overflow, but strata'srequire_fit(mtl_rt.mm:159-169) only checks threads per threadgroup, the other silent-skip mode. a one-linegrid[0]*grid[1]*grid[2] < 2^25warning inencode()(mtl_rt.mm:208-226) costs nothing and covers 2048-token prompt chunks if someone raises the chunk size. - GPU keep-alive after idle.
COLI_METAL_SPIN(backend_metal.mm:1060-1085) keeps a trivial CB in flight on a separate queue so the GPU doesn't clock down between bursts. strata's steady decode has no gaps worth it, but the first windows of a request after the server idled may be slow while DVFS ramps up. measure TTFT and the first 8 windows' ms after 60 s idle. if they're slow, runContext::warmfor ~50 ms when a request arrives, before prefill.
4. looked at and not worth taking #
| colibri mechanism | where | why not |
|---|---|---|
MTLHazardTrackingModeUntracked |
753, 928-929 | measured no difference (§2.1); would need fences around the overlap encoder |
MTLResidencySet replacing useResource: |
776-862, 995-1017, 1791-1802 | colibri needs it because it re-declares thousands of slabs per CB; strata binds ≤ 6 big buffers with setBuffer. nothing to amortize. (colibri's own E4 heap variant: −85 % GPU stall but +12 s disk-load regression; E5 never measured.) |
| passive OMP / host waits | METAL-M5MAX-PERF-REPORT.md | §2.2: passive event waits cost +130 µs per handshake; one spinning core is free |
F_RDADVISE / posix_fadvise(WILLNEED) read-ahead hints |
compat.h:30-48, qwen38_core.h:1523-1549 |
hints go into the unified buffer cache; strata does real async F_NOCACHE preads into the arena, deterministically retired (stream.cpp:656-716, limbo_) |
| F_NOCACHE storage probe with cold-range steering | colibri.c:11975-12164 |
useful only for picking a cache default by SSD speed; strata sizes the arena by memory (arena_budget.h) and the Air has one SSD |
| multi-SSD mirrors / bandwidth-weighted striping | docs/multidisk.md, deepseek_v4.c:336-515 |
one internal SSD on the laptop |
int4-rans256-g0 entropy-coded experts |
docs/int4-rans256-g0.md, rans.h |
§2.3: IQ2_XS bytes are ~99 % entropy |
| int4-g64 expert sidecar | docs/qwen38.md "Routed experts as int4-g64" |
strata already streams 2-bit-class packs (1.38–1.75 MB/expert vs their 2.76 MB) |
| async MoE begin/end ("resident work submitted before misses are read") | 1903-1936, docs/metal.md |
strata's resident-first split inside one CB (moe.metal:66-123, STRATA_METAL_EXPERT_SPLIT=1) is the same idea without a CPU round trip |
r_top8_par simdgroup top-k |
502-558 | strata's router_route does the softmax, top-10, MoCE and plan in one dispatch (router.metal:118) |
| fp8 e4m3 bit-trick decode, E8 lattice + FWHT | 126-144, 289-309, 345-364 | not our formats; the FWHT rotation trick is relevant only to rotated quantizers (QuIP#/E8), which GSQ-RCO isn't |
| offline residency simulator | docs/experiments/cnre-offline-simulator.md, tools/residency_sim.py |
we already have closed-loop MoCE-aware replays (audits/opus_strata_metal_opts_v1.md §1, audits/sol_moe_cache_policy.md), which colibri's tool explicitly doesn't model (no prefetch, no routing substitution) |
| prompt lookup drafts | spec_draft.h:182-198 |
= strata's suffix drafter (already wired, strata_window_cli.cpp:710-733) |
| verify-row state copies for GDN/PLE rewind | docs/speculative.md |
strata folds the kept prefix at commit (window.cpp:380, 716, 1120, 2211-2212), no per-row copies needed |
continuous batching / multiplexed rows (mux_rows) |
qwen38_core.h:2097-2102, decode_batch.h |
throughput across users; single-user latency is our target |
5. where strata is ahead (don't regress these) #
- one CB per decode window with per-layer event handshakes and the shared expert hidden between signal and wait
(
mtl_rt.mm:273-316,window.cpp:1610-1615), vs colibri's CB +waitUntilCompleted+ memcpy per layer or per op. - zero activation traffic to the host inside a window; colibri copies x in and out and scatter-adds expert outputs on the CPU.
- arena +
smapindirection (moe.metal:52-56): no per-dispatch address tables, nouseResourcelists, residency changes by writing one uint32. colibri rebuilds and uploadsgpuAddresstables for every MoE block (1771-1787). - resident-first expert split and MoCE in the router kernel, closed-loop tested; colibri's CACHE_ROUTE is CPU-side, opt-in and labelled experimental.
- I/O scheduling: urgent vs read-ahead queues with reserved urgent threads, one request per expert from the contiguous copy, a
miss split over threads, deterministic limbo retirement so SSD timing can't leak into routing (
stream.cpp:338-439,stream.h:105-110). - kernel efficiency: strata runs at 122–145 GB/s of a ~153 GB/s part (audits/opus_strata_metal_opts_v3.md); colibri's best
moe_gemvclaim is 358–389 GB/s (backend_metal.mm:265-266) on Max-class parts with 400–600 GB/s. relatively, ours is ahead, and it has fp32-free paths, matmul2d prompt tiles, constant-vs-threadgroup grid tables per format. - QSA flash / GDN kernels fused into the window; colibri's MLA path materializes S×H×T scores and caps out at 2^30 threads
(
backend_metal.mm:1509), its KDA step re-reads the whole state from device memory twice per token (652-690). - safe math by default for window/one-token bitwise identity (
mtl_rt.mm:80-88), plusrequire_fit(159-169) against silent threadgroup-size skips. colibri runs default math and documents prefill top-1 flips on near ties (docs/metal.md, #622). - the spin handshake: keep it (§2.2). if someone ports colibri's "passive wait" advice to
run_graph_seg, that costs ~6 ms/token.
6. colibri lessons worth writing down (not code) #
- they lost 39 % on the M5 Max to busy-waiting CPU threads stealing the SoC power budget. for us: keep exactly one spinner (§2.2).
- E4 (MTLHeap sub-allocation of expert slabs) cut GPU residency stalls 85 % but added +12–13 s of expert-load time, which they put
down to first-touch/lock contention on heap pages written by the CPU (
docs/experiments/e5-metal-residency-set.md). strata's arena is one wired anonymous mmap wrapped once (stream.cpp:530, 626-647). don't move it into anMTLHeap. - their DSv4.1 I/O study (
docs/experiments/dsv41-expert-io-2026-09-14.md) shows a 208-thread OpenMP team hid a 63 % I/O win behind libgomp spinning: check that the CPU side isn't the bottleneck before measuring an I/O change. our version is theSTRATA_METAL_PROFILE=1host-before / run_graph / GPU split.