{{ message }}
Repository navigation
feat(deepseek-v41): read the expert stream from more than one drive - #1522
Merged
Merged
Conversation
Owner
|
Reproduced the O_DIRECT half here before merging, since it changes the default. Released V4.1 Flash checkpoint, Ryzen 7 PRO 8700GE (8 cores), two NVMe in one md RAID0, cold page cache before every run, 26 prompt + 24 generated tokens, cap 8, OMP_NUM_THREADS=8, two runs per arm interleaved: Disk phase -40%, turn -12%, tokens identical across all six runs. The buffered arm of the PR is indistinguishable from dev, so the gain is the read path and not the slot refactor. The mirror split is not measurable on this box (both drives sit behind one RAID0, your M2 control), so I am taking your two-filesystem numbers as reported. Will merge once CI is green. |
The V4 engine learned COLI_MODEL_MIRROR and O_DIRECT reads years of
releases ago; deepseek_v41.c, its direct successor and the largest
container the project ships (510 GB), never got either. Both are about
the same fact: an expert is read once and never wanted again.
* COLI_MODEL_MIRROR / COLI_DISK_WEIGHTS / SNAP_MIRROR register
byte-identical read-only copies on other drives and split the experts
across them with a flat-index hash (the clustering fix deepseek_v4.c
documents). st_mirror_add already refuses a copy whose size or
safetensors header differs, so every offset stays valid on any
replica, and a partial mirror keeps the shards it does not carry on
the primary.
* COLI_MODEL_DIRS takes the other half of the same idea: DISTINCT
shards across drives, no second copy. At 510 GB that is the only
multi-drive layout most people can build, and the GLM, V4 and Kimi K3
engines already accept it.
* V41_DIRECT (default on, as in V4) reads the expert tensors with
O_DIRECT. The engine already fadvise(DONTNEED)s each expert after
reading it, which is the admission that a buffered read pays for the
bytes twice in order to leave the cache as cold as it already was.
The new shared primitive st_read_range_rep() in st.h does the replica-
aware, O_DIRECT-capable range read. O_DIRECT wants address, offset and
length block-aligned, and a safetensors range starts wherever the writer
put it; bouncing the transfer through a scratch buffer would add one copy
of every expert byte to a turn that moves 88.5 GB. The caller instead
reserves ST_DIRECT_ALIGN bytes of scratch BEHIND the destination, so one
aligned pread carries the block-aligned bulk and one short buffered pread
carries the tail, with nothing copied afterwards. The alignment is
verified rather than assumed, and a caller that did not reserve it, a
platform without O_DIRECT, or a replica without a twin all take the
buffered path, which reads exactly the same bytes.
Measured on the released checkpoint (208-core Xeon 8555C, PM9A3 and a
PM983 RAID0 pair), cold cache, interleaved, 3 runs per arm, every arm
token-exact against the same 16-token stream:
V41_DIRECT=0 -> 31.77 s of expert disk, V41_DIRECT=1 -> 20.35 s (-36%)
mirror across two controllers -> 17.11 s (-16%)
Validation:
- make -C c check (861 tests, including the new test_st_range_rep gate)
- the tiny oracle, token-exact at caps 1/2/8/49, with and without a
mirror, with and without COLI_MODEL_DIRS, and for both V41_DIRECT arms
- a CI step that holds the mirror, the split and the buffered path to the
same token stream as the primary
…t bounds it
Records the controlled A/B behind the previous commit, and the boundary
condition that matters more than the win.
Measured on the released 510.3 GB checkpoint, 208-core Xeon 8555C, cold
cache, arms interleaved, 3 runs each, every arm token-exact against the
same 16-token stream:
P one NVMe disk 20.29 s turn 408.8 s
M3 mirror, second controller disk 17.05 s turn 405.1 s
MP mirror, probe picks the split disk 18.83 s turn 403.1 s
M2 mirror, both copies behind
one RAID0 device disk 20.51 s turn 407.6 s
D0 buffered reads disk 32.10 s turn 418.0 s
So O_DIRECT is worth 2.3% of the turn and the split 1.4%. That is small
because the disk phase is 5% of it: the expert matmul is 46% and
attention 27%. The engine's own docs measure this container on a
16-thread box where disk (10.4 s) exceeds matmul (7.6 s); on 208 cores
the ratio inverts and nothing about the engine changed.
The M2 arm is the one worth reading twice. Two NVMe drives behind one
md0 device deliver exactly what one drive delivers, and c/iobench says
why: at 5.6 MB blocks and O_DIRECT one nvme0n1 already reaches ~6.2 GB/s
and the RAID0 pair reaches the same. The platform, not the drive count,
is the ceiling here, so the split that helped helped by giving the reader
a second filesystem to keep requests in flight against. Hypothesis JustVugg#2 is
not refuted, it is bounded: it pays when the host is I/O-bound.
Also recorded: the startup bandwidth probe (MP) came out worse than
pinning COLI_DISK_WEIGHTS=1,1 on a pair of equal drives -- 18.83 s against
17.05 s -- because it re-measures a split it does not need to on every
start, and the resulting disk times move with it.
The engine reaches 4.89 GB/s where the device answers at 6.03, and depth
4 beats the V41_READ_DEPTH default of 8 in isolation. Left open
deliberately: one host is not evidence for changing a default.
Two validated manifests (c/experiment_manifest.py) and the per-run engine
stderr they rest on are in docs/experiments/.
… it ran in The A/B in this record was measured with the engine's default OpenMP team, which on a 208-logical-CPU host is pathological: 93% of cycles inside libgomp and a turn that takes 409.9 s instead of 37.2 s (PR JustVugg#1517). It concluded that the disk phase is 5% of a turn and that this work is worth ~2.5%. That conclusion was wrong, and the correction is larger than the result it corrects. Re-run with the team sized (OMP_NUM_THREADS=32), same container, same prompt, same 16 reference ids, cold cache, arms interleaved, 3 runs each, every run token-exact 16/16: arm disk rate turn decode P one NVMe 16.07 s 6.17 GB/s 37.2 s 12.86 s M3 mirror, 2nd controller 12.66 s 7.83 GB/s 34.0 s 11.00 s M2 both copies on one RAID0 16.36 s 6.06 GB/s 37.9 s 12.96 s D0 buffered reads 31.14 s 3.18 GB/s 52.7 s 21.01 s So O_DIRECT is -48% of the disk phase and +63% of the decode rate, and the split is -21% and +17%. The engine now reaches the device's own ceiling -- 6.17 GB/s against the 6.34 GB/s iobench measures one nvme0n1 at depth 4 -- and M3 clears it at 7.83, which is only possible if the split is really using two devices. M2 stays a null, which is the control that says the variable is the number of independent filesystems, not the number of drives. The 208-thread numbers are kept, because they are real measurements of a real configuration and because the trap is that the phase breakdown looks entirely reasonable either way. Both manifests are regenerated from the team-sized runs and validated; V41_DIRECT +41% whole-turn, COLI_MODEL_MIRROR +12%.
LiRunGuo
force-pushed
the
feat/dsv41-expert-io
branch
from
September 15, 2026 12:50
c63da62 to
e761776
Compare
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.

Summary
deepseek_v4.chas hadCOLI_MODEL_MIRRORandO_DIRECTexpert reads for manyreleases.
deepseek_v41.c-- its direct successor, and the largest container theproject ships at 510 GB -- never got either. That is the "a mechanism reached one
engine and never its siblings" shape, twice, in the engine whose docs already say the
expert stream "is most of the engine".
Three changes, all about the same fact -- an expert is read once and never wanted
again:
COLI_MODEL_MIRROR/COLI_DISK_WEIGHTS/SNAP_MIRRORregister byte-identicalread-only copies on other drives and split the experts across them with a flat-index
hash (the clustering fix
deepseek_v4.cdocuments, including why an XOR of two smallids clustered the hot subset onto one replica).
st_mirror_addalready refuses a copywhose size or safetensors header differs, so every data offset stays valid on any
replica, and a partial mirror keeps the shards it does not carry on the primary.
COLI_MODEL_DIRSis the other half of the same idea and the cheaper one: thecontainer split across drives as distinct shards, no second copy. At 510 GB that
is the only multi-drive layout most people can build -- two ~256 GB drives instead of
one 512 GB -- and GLM, V4 and Kimi K3 all accept it while V4.1 did not. One line
(
st_init->st_init_multi), plus the docs and a gate.V41_DIRECT(default on, asCOLI_V4_DIRECTis in V4) reads the expert tensorswith
O_DIRECT. The engine already callsposix_fadvise(DONTNEED)on every expertafter reading it, which is the engine's own admission that a buffered read pays for
the bytes twice and then discards the cache it paid for.
The shared primitive, and why it is not a bounce buffer
st_read_range_rep()inst.his the replica-aware, O_DIRECT-capable range read.O_DIRECT constrains address, offset and length to the block size, and a safetensors
range begins wherever the writer put it.
deepseek_v4.candcolibri.csolve that byreading into an aligned scratch buffer and
memmove-ing the payload out; on a turnthat moves 88.5 GB of experts, that copy is most of what the change was for. This does
the opposite: the caller reserves
ST_DIRECT_ALIGNbytes of scratch behind thedestination, the read starts at the enclosing block boundary, one aligned pread
carries the block-aligned bulk and one short buffered pread carries the sub-block
tail. The payload lands exactly where the slot wants it and nothing is copied after.
The alignment is verified rather than assumed: a caller that did not reserve the
prefix, a platform without
O_DIRECT, a replica whose twin failed to open, and adevice that refuses the call all fall back to the buffered path, which reads the same
bytes.
tests/test_st_range_rep.cdrives that contract directly -- multi-block ranges,sub-block tails, the EOF clamp where the aligned window would run past the end, a
replica read, a divergent replica (refused), a replica with no copy of the shard
(stays on the primary), and a caller that reserved nothing. Guard bytes past the
payload prove the window arithmetic never writes outside it.
Measurements, and the mistake I made measuring them
Read the correction first, because it is bigger than the result. I first measured
this A/B with the engine's default OpenMP team, which on a 208-logical-CPU host is
pathological -- 93% of cycles inside libgomp, a turn that takes 409.9 s instead of
37.2 s (that is a separate defect, PR #1517). It said the disk phase is 5% of a turn
and this work is worth ~2.5%. Re-run with the team sized:
Pone NVMeM3mirror on a second controllerMPmirror, startup probe picks the splitM2mirror, both copies behind one RAID0D0buffered reads208-core Xeon 8555C, released 510.3 GB checkpoint, cold cache before every run, arms
interleaved, 3 runs per arm, medians, every run token-exact 16/16 in both regimes.
So
O_DIRECTis -48% of the disk phase and +63% of the decode rate, and the splitis -21% and +17%. Validated manifests:
dsv41-direct.json(+41% whole-turn),dsv41-mirror.json(+12%).Three things in that table are the point, not the percentages:
c/iobenchmeasures onenvme0n1at depth 4.M3clears it at 7.83, which is onlypossible if the split is genuinely using two devices.
M2is the control. Both copies behind themd0RAID0 pair is indistinguishablefrom one NVMe, so the variable is the number of independent filesystems, not the
number of drives. Two NVMe behind one device add nothing here; a second filesystem
adds 28% of read rate.
MPlands between the two with morevariance (52/52/51% of bytes to the mirror, disk 12.81 / 13.44 / 13.72 s). On a pair
of equal drives
COLI_DISK_WEIGHTS=1,1is the better choice; the docs alreadyrecommend that without saying why.
Full record with both regimes:
docs/experiments/dsv41-expert-io-2026-09-14.md. Rawper-run engine stderr:
dsv41-expert-io-2026-09-14-raw.txt.I kept the 208-thread numbers in the record rather than deleting them. They are real
measurements of a real configuration, the phase breakdown looks entirely reasonable in
both, and that is exactly why the wrong ratio was easy to publish. The record now leads
with a correction block.
Validation
make -C c check-- full suiteCOLI_MODEL_DIRS, and for bothV41_DIRECTarmsstream as the primary, and asserting a divergent mirror copy is refused rather
than trusted
upstream manifest; 36 runs across both regimes, every one token-exact
Compatibility
openflags, oneposix_memalignper cache slot, and a header-local helper
V41_DIRECT=1reads the same bytesby a different path,
COLI_MODEL_MIRROR/COLI_MODEL_DIRSare opt-in, and everyarm of the A/B is token-exact against the same stream
st_direct_fd_repreturns thecompat_open_directtwin, thealignment check falls back to buffered where
O_DIRECTdoes not exist, andposix_memaligniscompat.h's_aligned_mallocshimReviewer notes
st.hgains the primitive rather thandeepseek_v41.cowning it, because V4 and GLMneed exactly it and both currently carry their own copy-and-memmove version.
Migrating them is a separate, riskier change in much larger files.
COLI_MODEL_DIRScame along because it is the same user problem with a cheaperanswer; say the word if you would rather split it out.
measurable; neither needs the other to be correct, though the numbers in this record
were taken with
OMP_NUM_THREADS=32on the command line.