Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c24e926
convert: read Qwen3.8-Flash-Next into the existing container format
Skibisky Sep 4, 2026
6da8a47
tokenizer: state the digit run, and accept Qwen's pattern
Skibisky Sep 4, 2026
0b3a83e
qwen: the five architecture kernels, each against its own reference
Skibisky Sep 4, 2026
1454a39
model: the Qwen3.8-Flash-Next CPU forward pass
Skibisky Sep 4, 2026
dd9ad87
tests: a Qwen fixture, a container-native oracle, and the suite entries
Skibisky Sep 4, 2026
c43170d
docs: what Qwen3.8-Flash-Next support is, and what it measured
Skibisky Sep 4, 2026
c8040c6
tests: two suite rules that a Qwen container walked into
Skibisky Sep 4, 2026
434f17a
qwen: refuse a PLE conv kernel the ring cannot hold
mfethe1 Sep 13, 2026
0d1366f
Merge pull request #1 from mfethe1/pr63-ple-conv-fix
Skibisky Sep 13, 2026
6564cff
profile: Qwen's phases, and what they said about its cache
Skibisky Sep 10, 2026
d5a59f0
model: a Qwen layer's experts in one dispatch, once the cache holds them
Skibisky Sep 11, 2026
7da84df
model: Qwen's trunk through i8mm, and the harness that decided it
Skibisky Sep 13, 2026
0b92529
gitignore: the kernel_kl binary, beside sweep
Skibisky Sep 13, 2026
fcc78e8
model: keep the pool awake through HyperConnection
Skibisky Sep 13, 2026
3dc8570
model: GDN's recurrence, one value head per task
Skibisky Sep 13, 2026
4f9e316
model: QSA's attention, one query head per task
Skibisky Sep 13, 2026
760b5dd
model: QSA's RoPE rows once, its blocks at once, its top-k as a sort
Skibisky Sep 13, 2026
680219b
model: Qwen's routed experts staged by residency, not all-or-nothing
Skibisky Sep 14, 2026
7175bea
model: Qwen's router lookahead, from the next layer's normalized streams
Skibisky Sep 14, 2026
e46a74a
model: Qwen's routed experts in equal row ranges, three stages a layer
Skibisky Sep 14, 2026
48867c3
model: one quantization and one dispatch per vector Qwen projects
Skibisky Sep 15, 2026
ceea3fa
model: fewer places a Qwen step lets the pool fall asleep
Skibisky Sep 15, 2026
fa8c91e
model: QSA scores four selected tokens at a time, values through NEON
Skibisky Sep 16, 2026
8d5405d
Merge remote-tracking branch 'upstream/main' into feature/qwen38-flas…
Skibisky Sep 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ k3parts.bin
# containers a fuzzer kept for reproduction
fuzz-case-*/
sweep
kernel_kl
19 changes: 17 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ WASTE_CHUNK=1 ./test_forward ... # chunked prefill inste
./test_image /tmp && ./test_state MODEL && ./test_tokenizer MODEL "text"
./test_k3parts out.bin && uv run --with torch python tools/k3parts_ref.py out.bin

# two trunk kernels over a long prompt, every position: KL, argmax, routes,
# perplexity on the real text. Run 0 against 0 first — it must be all zero.
./kernel_kl MODEL ids.txt 256 0 2,3 512

# why two paths disagree: identical routes, a tie, or a real divergence
WASTE_DUMP_ROUTE=a.route WASTE_DUMP_SCORES=a.scores ./test_forward M IDS a.bin 0
WASTE_BACKEND=cpu WASTE_DUMP_ROUTE=b.route ./test_forward M IDS b.bin 0
Expand Down Expand Up @@ -127,6 +131,9 @@ fast group rather than waking the whole pool — 4 MB, measured; see
docs/LEARNED.md §67),
`WASTE_Q8=0` (dequantize the trunk to f32 at load, any width — 8x the RAM
on a 4-bit trunk, so it is out of reach on K3), `WASTE_I8MM=1`,
`WASTE_TRUNK_KERNEL` (the 4-bit trunk matvec: 0 f32, the exact reference;
1 SDOT; 2 i8mm, which a Qwen load selects when this is unset; 3 SMLAL —
LEARNED §77),
`WASTE_TOK_PLAIN=1`, `WASTE_VIS_STAGE`, `WASTE_DUMP_LATENT/HIDDEN`,
`WASTE_DUMP_DSA` (the sparse-attention selection: which pools won and on
what scores, so two implementations can be diffed on the decision rather
Expand All @@ -152,6 +159,12 @@ int8 lookup table raises the bar that far.

Profiling a decode step:
`WASTE_PROFILE=1 WASTE_CACHE_MB=17735 ./test_forward MODEL ids out.bin 5`.
`WASTE_PROFILE=decode` leaves the prompt steps out — they are the ones that
find the cache empty, so on a short run they are most of the expert I/O. A
Qwen container prints its own phase tree (HyperConnection, PLE, GDN, QSA,
router, shared expert) with ms/step; the gap between `wall` and `accounted`
is what no phase covers, and more than a few percent means a phase is
missing.

## Architecture

Expand Down Expand Up @@ -237,8 +250,10 @@ RSS actually stays inside the ceiling.
Residency also decides *scheduling*: `moe_layer` runs one task per routed
expert when the layer's experts are already cached and one per row range
when they are not, because holding K records before doing any arithmetic is
a barrier against the read-ahead. `WASTE_XPAR=0/1` forces it; the default
asks the cache. The two paths are **bit-identical** and `tests/run.sh`
a barrier against the read-ahead. `qwen_moe_layer` asks per expert instead:
the resident ones run first as tasks while the misses read, then the misses
(LEARNED §82). `WASTE_XPAR=0/1` forces it; the default
asks the cache. The paths are **bit-identical** and `tests/run.sh`
asserts it — an automatic choice that changed the numbers would make results
depend on how warm the cache happened to be.

Expand Down
20 changes: 16 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ CFLAGS += -MMD -MP

SRC := src/model.c src/kda.c src/backend.c src/ecache.c src/version.c \
src/tokenizer.c src/waste.c src/vq.c src/vision.c src/image.c \
src/crc32.c src/memory.c
src/crc32.c src/memory.c src/qwen_gdn.c src/qwen_qsa.c \
src/qwen_hc.c src/qwen_ple.c src/qwen_moe.c
# Match what backend.c tests for. Linux/aarch64 reports "aarch64", which
# does not contain "arm" — the old findstring left kda_neon.c out of the
# build while backend.c still emitted the call to it, so the link failed
Expand Down Expand Up @@ -258,9 +259,9 @@ waste$(EXE): cli/main.o libwaste.a
# the two failures tests/run.sh was written to catch, so a binary that
# `test` builds and `clean` forgets defeats the check meant to notice it.
TESTNAMES := test_kda test_container test_forward test_tokenizer test_k3parts \
test_state test_vision test_vision_glm test_vision_ds41 \
test_image test_memory \
test_cpus test_lock sweep
test_qwenparts test_state test_vision test_vision_glm \
test_vision_ds41 test_image test_memory test_cpus test_lock sweep \
kernel_kl test_qsa_pick test_qsa_attn
TESTBINS := $(addsuffix $(EXE),$(TESTNAMES))

test: $(TESTBINS)
Expand All @@ -284,10 +285,21 @@ test_forward$(EXE): tests/test_forward.o libwaste.a
# library the checks do and must never drift from it.
sweep$(EXE): tests/sweep.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)

kernel_kl$(EXE): tests/kernel_kl.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)

test_qsa_pick$(EXE): tests/test_qsa_pick.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)

test_qsa_attn$(EXE): tests/test_qsa_attn.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
test_tokenizer$(EXE): tests/test_tokenizer.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
test_k3parts$(EXE): tests/test_k3parts.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
test_qwenparts$(EXE): tests/test_qwenparts.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
test_image$(EXE): tests/test_image.o libwaste.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)

Expand Down
Loading