Correct the memory hierarchy: the earlier table measured the thread count, not the part - #32
Merged
Merged
Conversation
…ount, not the part
The hierarchy constants I added a few commits ago were wrong, and an occupancy probe written to explain
something else is what showed it. That table put the fast level's capacity at 2 MB with a fourfold cliff
past it. There is no cliff at 2 MB. Every size in it was measured at 64 threadgroups, which saturates DRAM
at 128 MB and starves an 8 MB working set by a factor of twelve:
working set 64 groups 512 groups
8 MB 196 2403
16 MB 160 1910
32 MB 131 447
So the "levels" between 4 and 32 MB were the thread count. Measuring each size at several threadgroup
counts and keeping the best moves the capacity from 2 MB to at least 16 MB and turns the far side from a
cliff into a ramp: 2453, 1795, 448, 175, 156 GB/s at 16, 32, 64, 128 and 256 MB. The thread count alone
moves 16 MB by 19.7x, which is why one count cannot serve the sweep.
The DRAM constant survives, and that was worth checking rather than assuming, because the corrected sweep
reads 156 GB/s at 256 MB against a recorded 120.6 and a constant that low would have reopened every decode
conclusion built on it. At a one-gigabyte working set bandwidth converges to 125 to 131 GB/s whatever the
occupancy, so 120.6 is right for true streaming and the 156 was partial residency. STREAMING_READ_GBPS
stands.
Also recorded, with a caveat that matters more than the numbers: how many threadgroups a resident working
set needs before it reads at resident speed, 256 at 4 MB rising to 1024 at 16 MB. Residency is necessary
and not sufficient. It is not yet shown to be actionable for real kernels -- the affine decode matmul
launches 35 to 280 groups depending on block_n, an eightfold range straddling those thresholds, and its
configs measure within 3% of each other, so whatever binds that kernel it is not this.
One test changed rather than being made to pass. It asserted the drop past the resident capacity was at
least threefold, which encoded the cliff the corrected measurement removed, so it was testing an artefact
of the harness. It now asserts the span and the ordering, which is what survives. A second test pins that
residency alone does not deliver resident bandwidth.
687 pass. Lint and vulture clean.
Co-Authored-By: Claude Opus 5 (1M context) <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 hierarchy constants I added a few commits ago were wrong, and an occupancy probe written to explain
something else is what showed it. That table put the fast level's capacity at 2 MB with a fourfold cliff
past it. There is no cliff at 2 MB.
Every size in it was measured at 64 threadgroups — enough to saturate DRAM at 128 MB, and starving an
8 MB working set by a factor of twelve:
So the "levels" between 4 and 32 MB were the thread count, not the memory system.
Corrected, sweeping occupancy per size and keeping the best
Capacity moves from 2 MB to at least 16 MB, and the far side becomes a ramp rather than a cliff.
The DRAM constant survives, and that needed checking
The corrected sweep reads 156 GB/s at 256 MB against a recorded
STREAMING_READ_GBPS = 120.6. Aconstant that low would have reopened every decode conclusion built on it — MLX at ~120 would be at 77% of
the limit, not 100%.
At a 1 GB working set bandwidth converges to 125–131 GB/s whatever the occupancy (256 → 2048
groups). So 120.6 is right for true streaming, and the 156 was partial residency. The constant stands and
the decode roofline conclusions hold.
Residency is necessary, not sufficient
Also recorded: threadgroups needed before a resident set reads at resident speed — 256 at 4 MB rising to
1024 at 16 MB.
With a caveat that matters more than the numbers: not yet shown to be actionable for real kernels. The
affine decode matmul launches
N / block_ngroups — 35 to 280 at N=8960, an eightfold range straddlingthose thresholds — and its configs measure within 3% of each other. Whatever binds that kernel, it isn't
this.
One test changed rather than made to pass
It asserted the drop past resident capacity was at least threefold, which encoded the cliff the corrected
measurement removed — it was testing an artefact of the harness. It now asserts the span and the ordering,
which is what survives. A second test pins that residency alone doesn't deliver resident bandwidth.
687 pass. Lint and vulture clean.
🤖 Generated with Claude Code