Repository navigation
STRATA_EXCHANGE_ROTATE: rotation for packs whose layers differ in expert block size (spares kept per size) - #2042
Xiaomi2008 wants to merge 1 commit into
Conversation
…ert block size (spares kept per size)
Rotation hands the evicted expert's exchange buffer over as its place in the
RAM copy instead of copying the bytes there. It required one block size for
all layers and fell back to the copy path on any other pack: Swift 1.5 IQ3_S
has seven sizes (1,510,400 to 2,662,400 bytes) and IQ3_XXS nine.
A swap exchanges two experts of one layer, so the slot the promoted expert
leaves has the size the evicted one needs. ExchangeStorage now keeps its
spares per block size (initialize_sized): an exchange takes a spare of its
layer's size and hands back one of the same size. commit() refuses a spare of
another size and an expert of another layer. initialize() keeps the uniform
geometry and its checks. exchange_buffer() takes the layer; reserve_exchanges()
takes the number of trailing buffers that only stage copies (--adapt-async's
second half), kept in the largest size alone. With --adapt-async 1 a rotated
round flips in the tick that sees the copies in: there is nothing for the job
to move, so the round ends one verify window sooner.
Still opt-in (STRATA_EXCHANGE_ROTATE=1); unset, nothing changes. The exchange
allocation grows to one spare of every size per swap of a round: 1.06 GiB of
page-locked RAM on IQ3_S with --adapt-swaps 80 (1.26 GiB with --adapt-async 1)
against 0.20 and 0.40 GiB.
Measured on a Core i7-13700K, RTX 5090 32 GB, 64 GB, Windows 11, Swift 1.5
IQ3_S, 128K, --resident-experts, --adapt-every 1 --adapt-swaps 80, p-cores:
one 8K prompt, the same 1,500 forced tokens, blocking tier, rotation off / on:
161.6-165.1 tok/s (five runs) / 184.9-188.7 (four runs); two runs of each
arm with the same cache size (10,681 slots) wrote identical --window-hashes
for all 434 windows
--serve, 23 coding-agent turns, greedy, three interleaved rounds, blocking tier:
147.8 / 159.2 / 158.4 (the build before) -> 175.2 / 184.9 / 172.8
the same turns with --adapt-async 1, one binary, two rounds:
166.0 / 170.1 -> 185.6 / 182.5
The tier swaps about 30 experts per verify window on this box (44% of the
experts fit in VRAM), so the copy path moved about 50 MiB of RAM per window.
tests/core/exchange_storage_test.cpp gains a three-size case (6,144 exchanges)
and passes built alone with MSVC 14.50. Built for CUDA only (sm_120, CUDA
13.0.88, MSVC 14.50 with nvcc's --allow-unsupported-compiler); HIP and SYCL
were not compiled (expert_source.cpp and generate.cpp are built by both). Not
measured: Linux, AMD cards, a layer split.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ert block size (spares kept per size)
Rotation hands the evicted expert's exchange buffer over as its place in the
RAM copy instead of copying the bytes there. It required one block size for
all layers and fell back to the copy path on any other pack: Swift 1.5 IQ3_S
has seven sizes (1,510,400 to 2,662,400 bytes) and IQ3_XXS nine.
A swap exchanges two experts of one layer, so the slot the promoted expert
leaves has the size the evicted one needs. ExchangeStorage now keeps its
spares per block size (initialize_sized): an exchange takes a spare of its
layer's size and hands back one of the same size. commit() refuses a spare of
another size and an expert of another layer. initialize() keeps the uniform
geometry and its checks. exchange_buffer() takes the layer; reserve_exchanges()
takes the number of trailing buffers that only stage copies (--adapt-async's
second half), kept in the largest size alone. With --adapt-async 1 a rotated
round flips in the tick that sees the copies in: there is nothing for the job
to move, so the round ends one verify window sooner.
Still opt-in (STRATA_EXCHANGE_ROTATE=1); unset, nothing changes. The exchange
allocation grows to one spare of every size per swap of a round: 1.06 GiB of
page-locked RAM on IQ3_S with --adapt-swaps 80 (1.26 GiB with --adapt-async 1)
against 0.20 and 0.40 GiB.
Measured on a Core i7-13700K, RTX 5090 32 GB, 64 GB, Windows 11, Swift 1.5
IQ3_S, 128K, --resident-experts, --adapt-every 1 --adapt-swaps 80, p-cores:
one 8K prompt, the same 1,500 forced tokens, blocking tier, rotation off / on:
161.6-165.1 tok/s (five runs) / 184.9-188.7 (four runs); two runs of each
arm with the same cache size (10,681 slots) wrote identical --window-hashes
for all 434 windows
--serve, 23 coding-agent turns, greedy, three interleaved rounds, blocking tier:
147.8 / 159.2 / 158.4 (the build before) -> 175.2 / 184.9 / 172.8
the same turns with --adapt-async 1, one binary, two rounds:
166.0 / 170.1 -> 185.6 / 182.5
The tier swaps about 30 experts per verify window on this box (44% of the
experts fit in VRAM), so the copy path moved about 50 MiB of RAM per window.
tests/core/exchange_storage_test.cpp gains a three-size case (6,144 exchanges)
and passes built alone with MSVC 14.50. Built for CUDA only (sm_120, CUDA
13.0.88, MSVC 14.50 with nvcc's --allow-unsupported-compiler); HIP and SYCL
were not compiled (expert_source.cpp and generate.cpp are built by both). Not
measured: Linux, AMD cards, a layer split.
Port of PR #2042 (commit 8db3750).
|
Thanks, nice patch. We ported it with your authorship onto 0.1.44.1 and measured it on an RTX 3060 12 GB with the IQ3_XXS pack in resident RAM mode: decode 58.96 -> 62.03 tok/s (+5.2%, 5 of 5 interleaved pairs faster), prompt speed unchanged, and the tokens are identical to the build without it when the adaptive tier swaps (checked on a Tesla P100 with the same pack). It stays an opt-in switch for 0.1.45 because it needs about 1.4 GiB more page-locked RAM; we will look at making it the default after the combined test. |
|
Thanks for the pull request. Your change is already in If part of your work did not make it in and you want it considered, open a new pull request on top of |
Follow-up to #864 (the opt-in exchange buffer rotation). I searched the open threads: #1340 and #1838 touch the resident RAM exchanges but not this limit.
What it changes
STRATA_EXCHANGE_ROTATE=1now works on packs whose layers differ in expert block size. Until now rotation required one block size for all layers and fell back to the copy path otherwise ("requires equal-size expert blocks"). Swift 1.5 IQ3_S has seven sizes (1,510,400 to 2,662,400 bytes) and IQ3_XXS nine, so neither could rotate.ExchangeStoragekeeps its spare buffers per block size (initialize_sized). A swap exchanges two experts of one layer, so an exchange takes a spare of its layer's size and hands back one of the same size; every size keeps its number of spares.commit()refuses a spare of another size and an expert of another layer.exchange_buffer()takes the layer;reserve_exchanges()takes the number of trailing buffers that only stage copies (--adapt-async's second half), kept in the largest size alone.--adapt-async 1a rotated round flips in the tick that sees the copies in (nothing for the job to move), one verify window sooner.What it leaves alone
Rotation stays opt-in. Unset, nothing new runs: the copy path, the uniform
initialize()geometry and its checks are as before, and no default or preset changes. Switched on, the exchange allocation is one spare of every size per swap of a round: 1.06 GiB of page-locked RAM on IQ3_S with--adapt-swaps 80(1.26 GiB with--adapt-async 1) against 0.20 and 0.40 GiB.Measured
Core i7-13700K, RTX 5090 32 GB, 64 GB DDR5, Windows 11, driver 617.42. Swift 1.5 IQ3_S, 128K,
--resident-experts(about 11,000 of 24,576 experts in VRAM),--adapt-every 1 --adapt-swaps 80 --adapt-decay 0.97,--spec 4,--pool-affinity p-cores.This branch (main at 7695859 + this commit, engine 0.1.43), same day, one binary, only the variable differs. One 8K prompt, the same 1,500 forced tokens (
--spec-follow), blocking tier, runs interleaved:STRATA_EXCHANGE_ROTATE=1An unset run and two rotated runs that sized the cache alike (11,099 slots) wrote the same
--window-hashesfile for all 434 windows. (The unset run that sized 11,017 slots differs from the others, as any other cache size does.)The same commit on 0.1.42 (my fork's main, which also carries #1911 and #1912):
--serve, 23 coding-agent turns (tools/agent_turns_bench.py's turns, greedy, up to 400 new tokens), three interleaved rounds, blocking tier (off = the build before)--adapt-async 1, one binary, two roundsThe tier swaps about 30 experts per verify window all through a run on this box, so the copy path moved about 50 MiB of RAM per window on the decode thread.
How it was checked, and what was not
tests/core/exchange_storage_test.cppgains a three-size case (6,144 exchanges: bytes, aliases, the refusals, overlapping resident blocks) and passes on this branch, built alone with MSVC 14.50. I did not run it under ASan/UBSan, and I did not runfile_expert_source_test --rotation-gpu.--allow-unsupported-compiler). HIP and SYCL were not compiled;expert_source.cppandgenerate.cppare built by both.Details and the numbers:
docs/EXCHANGE_ROTATION.md.🤖 Generated with Claude Code