Repository navigation
Description
What happens
On three GB10 hosts (DGX Spark class, unified memory, one rank per host), an export of DeepSeek V4.1 Flash with tensor parallelism 3 at 0572fd12 stops 17.3 to 18.9 s into "Prepare and tune kernels" on every rank. Rank 0 runs python -m ignition export deepseek-v4.1-flash --ranks 3 --rendezvous <rank 0>:<port> --config <file>, and each peer runs python -m ignition join <rank 0>:<port> --rank <1|2> --ranks 3 --gpu 0 --config <file>. The file sets only [paths] kernel_cache and core_cache; everything else is a recipe default. Each rank reports:
b12x.preparation.types.CandidateResourceIneligibleError: no eligible configurations for moe.decode within the session scratch limit of 0 bytes
RecordedPreparationSession._reserve_scratch (ignition/preparation/tuning.py:387) bounds the scratch of kernel candidates with candidate_scratch_limit (ignition/preparation/host_memory.py:153). On an integrated GPU that limit is MemAvailable minus the greater of the 8 GiB floor (_HEADROOM_BYTES) and the plan's headroom (1 GiB here), minus 1 GiB of tuning growth and the persistent bytes of plans not yet materialized (envelope.pending_persistent_nbytes), clamped at zero; the minimum across ranks applies. With the limit configured as 0, the second requirements() call (tuning.py:434) reaches FlashInfer's candidate_memory_envelope, which raises because no moe.decode configuration fits. Both Ignition functions are the same at bbad44fe; what differs is the memory available at that point, measured below on bbad44fe plus #122.
MemAvailable was sampled every 2 s on each host. The comparison export is bf5ca911, the head of #122 from 10 to 12 November: one commit on bbad44fe, a preparation-only change that makes the 8 GiB floor a setting, run with --compile-headroom 6G; it completed. The setting changes the limit, not what is allocated before it. "Before graph catalog" is the last sample before the steep fall, which begins within one sample of the start of "Construct graph catalog" (phase starts are timestamped in the peers' logs and summed from phase durations on rank 0). "Tuning, before the reservation" is the samples from the start of "Prepare and tune kernels" to the scratch reservation (_reserve_scratch): at 0572fd12 to the failure, at bf5ca911 and f25a0386 to a 1.1 to 1.2 GiB step in MemAvailable 13 to 17 s after the phase start, taken as the arena allocation.
| Export | Rank | Before graph catalog (MiB) | Tuning, before the reservation (MiB) |
|---|---|---|---|
0572fd12, default |
0 | 19691 | 7314 to 7590 |
0572fd12, default |
1 | 20350 | 7812 to 8083 |
0572fd12, default |
2 | 20389 | 7756 to 7970 |
bf5ca911, 6 GiB |
0 | 19644 | 11492 to 11588 |
bf5ca911, 6 GiB |
1 | 20529 | 12012 to 12280 |
bf5ca911, 6 GiB |
2 | 20531 | 12082 to 12195 |
Every host at 0572fd12 is below the 8192 MiB floor, so the limit is 0 whatever the pending bytes are; a limit above zero needs more than 9216 MiB plus those bytes, 1133 to 1902 MiB more than the hosts had. Each host has 3902 to 4468 MiB less available before the reservation at 0572fd12 than at bf5ca911. Swap use changed by at most 26 MiB on any host between the two columns.
At bbad44fe with the default floor, two exports on these hosts completed kernel tuning (4268.2 s and 404.2 s; #242 comment, #122 comment). Those reports give no MemAvailable at the reservation; at the 11492 to 11588 MiB of the lowest host at bf5ca911, the default floor leaves a limit of 2276 to 2372 MiB less the pending bytes.
Why it matters
With recipe defaults, an export at 0572fd12 stops at a point that two exports at bbad44fe passed on the same hosts, and the error reports a 0-byte limit without the memory found or required. The failure was observed in one export at 0572fd12; an export with the same ignition/ plus #122 reached the same memory level on rank 0's host (the --compile-headroom 4G export under "Possible direction"), and the shortfall exceeds the spread of the samples. #73 reports the same 8 GiB plus 1 GiB threshold stopping kernel tuning on GB10 for a GLM-5.3-Flash TP2 export with MTP; its 9,216 MiB MemoryError is the one tuning_limits raises.
Where the memory goes
When tuning starts, ranks 1 and 2 log the growth of torch.cuda.memory_allocated while each graph was constructed, summed by graph kind ("Graph operand allocation by operation", ignition/recipes/deepseek41/bootstrap.py, same code in both commits). The two ranks report the same bytes:
| Graph kind | bf5ca911 (MiB) |
0572fd12 (MiB) |
|---|---|---|
| prefill | 125.6 | 2665.6 |
| speculative | 2700.2 | 3434.2 |
| vision | 0.0 | 789.6 |
| resume | 7.0 | 85.7 |
| decode | 78.5 | 38.4 |
| Total, with init, restore and snapshot | 2911.4 | 7013.5 |
The per-kind rows do not compare, and the totals compare to within about 151 MiB. At bbad44fe one ScratchPool backed two GraphBufferPools (bootstrap.py:414-418) whose cursor each graph and prefill layer forked into overlapping storage, so a pool chunk is charged to the first graph that grows it, and the shared suffix and window-staging buffers (bootstrap.py:429, :432-434; 63 and 88 MiB in the table below) are allocated before the counter starts. The total is 4102 MiB larger at 0572fd12, about 3950 MiB after that offset; both are within the 3902 to 4468 MiB by which MemAvailable is lower before the reservation. The part of the fall that the logged growth does not cover is 5088 to 5619 MiB on every host in both commits, taking for rank 0 the growth that ranks 1 and 2 logged. When tuning started at 0572fd12, ranks 1 and 2 reported 9645 MiB allocated and 9666 MiB reserved, so cached free blocks held 21 MiB.
That pool was replaced by compiler-planned temporaries in 4a24d78, merged through 2b38bf9 and e5e7881. 4a24d78 reports DeepSeek V4.1 on TP4 and states that "no matched serving-memory comparison was run". At 0572fd12 the example operands that operator declarations keep for tuning (declaration tensors) are alive across the reservation: finalizers registered through register_finalizer (tuning.py:746-753) release them after all tuning jobs (tuning.py:596-597), and the recipe's own releases (bootstrap.py:580-588) follow session.tune.
Allocation sites that are per decoder or per layer at 0572fd12 and came from the shared pool at bbad44fe, with sizes computed from tensor shapes in the code (10 prefill capacities in 2 modes, 64 packed verification shapes with a batch sum of 683, 40 layers), not measured:
Allocation at 0572fd12 |
0572fd12 (MiB) |
bbad44fe (MiB) |
|---|---|---|
Verification window staging, one (40, batch, 136 × 528) byte buffer per verification decoder (components/ring_commit.py:60-64); bbad44fe shares one |
1871 | 88 |
128-row suffix Scratch for each of 18 packed prefill decoders (recipes/deepseek41/decoder.py:263-264); bbad44fe shares one |
1141 | 63 |
Compressor operands per KV-source layer and decoder (recipes/deepseek41/attention.py:151-167) |
672 | 20 |
Prefill window cache per layer and decoder (attention.py:308-309) |
456 | 2 |
Indexer packed queries per selector (components/mxfp4_indexer.py:62-65) |
297 | 0.3 |
VisionScratch and PreparedVision operands per patch capacity (bootstrap.py:519-525) |
not sized | not sized |
Restricted to prefill decoders, the suffix, compressor, window cache and indexer sites compute to about 2485 MiB, of the 2666 MiB logged for prefill. Verification staging plus 76 MiB of compressor and indexer operands of verification decoders compute to 1947 MiB, of the 3434 MiB logged for speculative; the rest was not itemised.
Not measured
pending_persistent_nbytesat the reservation on either commit.MemAvailableat this point atbbad44fewith the default floor.- Which tensors make up each graph kind's storage; the per-site sizes are computed from shapes.
- Why
MemAvailablefalls a further 214 to 276 MiB on every host in the 17 s of tuning before the reservation at0572fd12(96 to 268 MiB atbf5ca911). - Whether the contents of the kernel cache change the scratch that tuning requires.
- Other models, tensor-parallel widths and hosts.
Possible direction
- Give the per-decoder and per-layer declarations above shared backing through
declaration_tensorroles (ignition/preparation/examples.py). 07e94ca made that helper reuse one allocation per role across capacities because "Retaining separate declaration tensors for every graph capacity can exhaust device memory before tuning"; in this recipe it covers MXFP8 activation staging and MLA norm-quantization outputs, none of the sites above. - Release the declaration tensors that the reservation and candidate measurement do not use before the reservation, instead of after all tuning jobs.
- Report the memory found and required when the limit is zero.
tuning_limitsalready raises with both numbers (host_memory.py:185-187), but it runs after_reserve_scratch(tuning.py:512against:503).
#122's --compile-headroom gets past the failure but leaves the host short of memory. With --compile-headroom 4G on f25a0386 (#122 on 0572fd12, ignition/ otherwise identical), rank 0's host had 7256 to 7483 MiB before the reservation, within about 110 MiB of the 7314 to 7590 MiB of the 0572fd12 export and against 11492 to 11588 MiB at bf5ca911. The export passed the reservation; those samples give a limit of 2136 to 2363 MiB less the pending bytes on that host. The floor is an input to the scratch limit, the tuning limits and compiler admission, not a level the host is held above: during kernel tuning that host fell to 3381 MiB available (56 of 2186 samples below 4096 MiB) and its swap use grew from 628 to 4456 MiB. That host also runs processes outside the export, and which pages moved to swap was not examined. An earlier 6 GiB export of bf5ca911, not the one in the table, spent 4271.5 s in kernel tuning and is reported in the #122 comment with a minimum of 7018 MiB on rank 0's host and swap growing from 374 to about 3000 MiB during kernel tuning; the two exports differ in floor and in kernels measured, so they do not isolate the effect of the graph operand storage. The 4 GiB export completed in 9527 s: kernel tuning took 4611.9 s, all 38 forwards compiled in 1848.1 s with one compiler worker per host, and the core was written. After kernel tuning that host's lowest sample was 6768 MiB, and its swap use peaked at 5609 MiB during compilation.
Environment
- Three GB10 hosts (DGX Spark class, unified memory,
MemTotal121.6 GiB), one rank per host. - Checkpoint
deepseek-ai/DeepSeek-V4.1-Flash-1017(revision1bae0e10), recipe defaults,compile_workersunset. - Page caches dropped before each export;
MemAvailable115.8 to 116.6 GiB before the start. The kernel cache directory held results of earlierbbad44fe-based exports.vm.min_free_kbytesis 45154 on rank 0's host. 0572fd12(failure),bf5ca911with--compile-headroom 6G,f25a0386with--compile-headroom 4G; FlashInfer as pinned by each tree (7b35510bat0572fd12,85ea6e76atbbad44fe).
Activity
javieratomic commentedon Nov 12, 2026
Measured at the scratch reservation:
0572fd12andbbad44fe, default floorThe start of the export was repeated for both commits on the same three hosts with recipe defaults (no
--compile-headroom), page caches dropped before each, up to the scratch reservation. On every rank, a wrapper aroundRecordedPreparationSession._reserve_scratchrecorded the meminfo counters, the torch allocator counters and every live CUDA tensor storage the garbage collector tracks (de-duplicated, grouped by dtype and shapes, with referrers for a sample of each group) before the original method ran, plus the arguments and result of eachcandidate_scratch_limitcall inside it.0572fd12failed with the reported error on every rank;bbad44fepassed the reservation.Limit arithmetic
candidate_scratch_limitwas called once per rank withpools=1,headroom_bytes1 GiB andpending_persistent_bytes198,653,960 (189.5 MiB), the same on every rank in both commits.MemAvailableread immediately after the call reproduces each result to the byte asMemAvailable − 8192 MiB − 1024 MiB − 189.5 MiB, clamped at zero.MemAvailableat the call (MiB)bbad44febbad44febbad44fe0572fd120572fd120572fd12At
bbad44fethe reservation required 1282.8 MiB (candidate_scratch_bytes) against the 2115.1 MiB minimum, andMemAvailablefell 1087 to 1114 MiB across_reserve_scratch. At0572fd12a limit above zero needed more than 9405.5 MiB, 1406 to 2134 MiB more than the ranks had.MemAvailableat the call is 4015 to 4251 MiB lower at0572fd12, and the export process'sVmRSSon rank 0 is 4034 MiB higher.torch.cuda.memory_allocatedandmemory_reservedat0572fd12are 9645.9 and 9666.0 MiB on all three ranks. The allocator counters do not compare across the two commits (about 100951 MiB allocated atbbad44fe, with the same weights and caches alive), so the comparison below uses storages.Where the memory goes, measured
Tracked CUDA storage is 4273.1 MiB larger at
0572fd12on every rank. Storages of the same byte size in both inventories cancel; that includes the KV, indexer-cache and weight storages, which changed owner or view layout but not size. Rank 0, with the computed sizes from the issue; ranks 1 and 2 give the same bytes and counts:0572fd120572fd12(MiB, storages)bbad44fe(MiB)(40, batch, 71808)uint8Scratchof 18 packed prefill decoders(tokens, 512)withPreparedCompressorowners); smaller shapes not listedVisionScratchandPreparedVisionoperands, 5 patch capacitiesVisionScratch.allocate; not sized in the issue)At
bbad44feonly the staging buffer has a storage of its own; in the code the other sites take cursors of the sharedGraphBufferPool, so they cannot be sized separately there. The vision row compares with the 789.6 MiB logged for vision in the issue's graph-kind table.Not in the issue's table:
bbad44fe, or larger there, 1519.4 MiB, including speculative sampling buffers (probabilities, top-p workspaces, logits and log-probability keys, about 982 MiB),PreparedTableRequests.token_mapvectors (161.8 MiB; 166 storages against 2) and per-graph page tables (about 270 MiB).0572fd12besides the sites, 548.1 MiB, including the 224-row draft scratch (about 163 MiB) and three more 40 MiB prefill scratch storages (9 against 6).0572fd12against 11.0 MiB atbbad44fe.Sites +5066.8 MiB, the rest of the listed storage −971.3 MiB and the unlisted groups +177.5 MiB add up to the +4273.1 MiB within rounding, against
MemAvailable4015 to 4251 MiB lower at the call.Still not measured
bbad44feother than the staging buffer.MemAvailablebefore the reservation. At0572fd12it fell 30 to 239 MiB between the start of the inventory and the limit call, but the inventory itself ran in that interval.original-el8 commentedon Nov 13, 2026
Categorized as a P2 preparation-memory investigation. The default export reaches kernel tuning below the headroom floor, so candidate scratch is bounded to zero. #122 supplies a configurable floor, but its successful lower-floor export does not explain or remove the additional graph-catalog memory reported here.
Track the live allocations between catalog construction and scratch reservation, including pending persistent plans, on each rank. Keep #242's compiler-admission failure separate from this earlier tuning failure. A successful reduced-floor run must also report swap and memory pressure; it is not evidence that the default allocation budget is repaired.