Skip to content

DeepSeek 4.1 TP3 on GB10: 0.46 GiB/GPU less prefill scratch gave +7.6 % KV tokens (measurement only) #186

Open

Description

@javieratomic

On three GB10 hosts at TP3, DeepSeek V4.1 Flash starts with about 7.7M KV tokens (flare_total_kv_tokens). A local prototype that reduced the fixed prefill scratch of the DeepSeek 4.1 recipe by 0.46 GiB per GPU raised that to about 8.3M (+7.6 % on the mean of three starts), with bit-identical first-token log-probabilities. I'm reporting the measurement so you can judge whether the headroom is worth pursuing. I'm not proposing an implementation: the prototype was written with Claude Opus 5.5 and falls under the model-optimization restriction in AGENTS.md and principle 14 of the design review, so I won't submit it or describe it.

Setup. main at f571a550. Both packages were exported with --max-concurrency 4 --mtp-draft-tokens 5 and the default 4096-token prefill chunk, with the preparation host-memory floor lowered from 8 GiB to 6 GiB by a local override (main has no option for it). Two export attempts closer to the recipe defaults (max concurrency 32, 7 draft tokens) failed on these hosts: one at the host-memory check before compilation (12,269 MiB available, 12,288 required), one at a RoCE collective timeout while warming compiled forwards. Both packages were served with flare run from the same main build. Each package was started three times: the first start of each was a cold start without a fixed idle period; the second and third alternated between the packages, each after 3 minutes idle. The prototype's first start attempt failed at "Distribute rank packages over CX7" (failed to fill whole buffer) and started normally on retry.

Decode and prefill in tok/s, aggregate over the streams, mean and range of 3 starts:

main Prototype Difference
Fixed device memory per GPU (flare inspect --memory-map) 96.71 GiB 96.25 GiB -0.46 GiB
flare_total_kv_tokens (3 starts) 7,915,264 / 7,456,512 / 7,762,304 8,505,088 / 8,213,760 / 8,172,672 +7.6 % on the mean
Decode prose x1 45.5 (44.5–46.3) 44.2 (43.7–44.8) -2.9 %
Decode prose x4 89.7 (88.3–91.7) 92.3 (90.3–94.6) +2.9 %
Decode code x1 82.6 (79.4–88.2) 83.1 (80.3–87.0) +0.7 %
Decode code x4 150.4 (142.4–154.9) 149.2 (144.0–157.5) -0.8 %
Prefill, 30k-token prompt 2,764 (2,540–2,891) 2,776 (2,557–2,900) +0.4 %
Prefill, 120k-token prompt 3,390 (3,281–3,448) 3,298 (3,205–3,421) -2.7 %

Every decode and prefill difference is within the range of single starts, but prose x1 and 120k prefill were lower with the prototype in two of the three alternating pairs; three starts cannot separate that from noise. The KV total is sized at start from the memory then free and varied by up to 0.46M tokens between starts of the same package; the lowest prototype start (8.17M) is still above the highest main start (7.92M). At 890 bytes per token per GPU, 0.46 GiB corresponds to about 0.55M tokens; the measured mean difference is 0.59M.

The top-20 log-probabilities of the first generated token were bit-identical between the first start of each package for ten prompts from 836 to 94,341 tokens, and a 118k-token retrieval test passed 16/16 in all six starts.

The saving scales with the prefill chunk: a layout calculation for the prototype gives about 110 MiB per 1024 tokens of chunk, about 440 MiB at 4096, close to the 468 MiB measured. I have not measured other chunk sizes on this base or other recipes.

To reproduce the measurement on main.

  1. Export at TP3, with ignition join <rank0>:<port> --rank {1,2} --ranks 3 --gpu 0 on the peers:
    ignition export deepseek-v4.1-flash --ranks 3 --rendezvous <rank0>:<port> --gpus 0 \
      --max-concurrency 4 --mtp-draft-tokens 5
    
    For both packages we lowered ignition.preparation.host_memory._HEADROOM_BYTES to 6 GiB; we did not try this recipe with the default 8 GiB.
  2. Fixed device memory per rank: flare inspect --unverified --memory-map --json <package>/model.safetensors, summing ranks[*].allocations[*].nbytes.
  3. Serve with flare run <package> --rank 0 --rendezvous <rank0>:<port> --gpus 0 --scheduler-policy throughput --prefix-cache-memory 0 --prefix-cache-disk <dir> --prefix-cache-disk-limit 200G (peers: flare run --rank {1,2} ...), after sync; echo 3 > /proc/sys/vm/drop_caches on every host, and read flare_total_kv_tokens from /metrics once /v1/models answers. On GB10 the KV pool is sized from MemAvailable at start, so compare several alternating starts rather than one.
  4. Decode: 512 generated tokens per stream, prose and code prompts, 1 and 4 concurrent streams. Prefill: time to first token for 30k- and 120k-token prompts.

If you look into it, I can export and measure any branch on the same three hosts with the same protocol.

Activity

  1. added
    area:memoryMemory budgets, allocation lifetimes, scratch sharing, and capacity.
    area:preparationIgnition checkpoint loading, tuning, compilation, capture, and export.
    performancePerformance, latency, throughput, or memory-efficiency investigation or improvement.
    triage:design-reviewNeeds a maintainer scope, architecture, or contract decision before implementation or integration.
    on Nov 5, 2026
  2. original-el8 commented on Nov 5, 2026

    @original-el8
    Collaborator

    Categorized as performance, area:memory, area:preparation, and triage:design-review. The demonstrated result is increased KV capacity from reduced fixed scratch, not a demonstrated throughput improvement. The next decision is whether that capacity saving warrants an implementation under the repository's design requirements; any implementation needs its own lifetime analysis, numerical checks, and fresh matched measurements. This is not a confirmed correctness bug or a closure-verification item.

  3. added a commit that references this issue on Nov 6, 2026
  4. added
    model:deepseek-v4.1Affected recipe or documented reproducer: deepseek-v4.1. Impact may be broader.
    on Nov 8, 2026
  5. javieratomic commented on Nov 12, 2026

    @javieratomic
    ContributorAuthor

    Update for main at 0572fd12: the scratch this issue measured is gone. 4a24d78 (merged through 2b38bf9 and e5e7881) replaced the DeepSeek V4.1 recipe's manual scratch pools with compiler-planned temporaries. A core exported with recipe defaults from 0572fd12, which includes that change among other storage changes since bbad44fe, holds 4.7 GiB less fixed device memory per GPU than one exported from bbad44fe on the same three GB10 hosts (TP3, one rank per host). The difference is between the two commits, 39 first-parent commits apart, not the effect of 4a24d78 alone; the message of e5e7881 claims no fixed-memory reduction for the merged revision.

    Memory inventory printed at export, rank 0, in MiB (ranks 1 and 2 are within 4 MiB on both commits):

    bbad44fe + #122 0572fd12 + #122
    Core fixed mappings 101,644 96,826
    Weights / numeric constants 95,804 95,556
    Graph scratch pools 2,640 none
    Operator scratch 220 none
    Other owned tensors 2,333 207
    Graph capture pool 202 768
    Allocator slack 181 12

    At load, with the same flare run options (--scheduler-policy throughput --kv-cache-limit memory --prefix-cache-memory 0 --prefix-cache-disk <dir> --prefix-cache-disk-limit 50G, default draft tokens), rank 0 reports the following in "Plan cache" and "Allocate cache". The bbad44fe cores were started by the bbad44fe runtime with #251 (e774a46) applied, without which they did not start, and the 0572fd12 core by the 0572fd12 runtime; the two sides were started on different days.

    Core Loaded per GPU (GiB) Cache budget (GiB) KV tokens
    bbad44fe + #122, 6 starts of 2 cores 103.59 to 104.69 1.95 to 3.16 1,456,256 to 2,927,232
    0572fd12 + #122, 3 starts 100.17 to 100.33 6.27 to 6.30 6,670,080 to 6,713,728

    With --mtp-draft-tokens 0 the 0572fd12 core loaded 98.09 to 98.14 GiB and allocated 9,992,192 to 10,107,008 KV tokens in two starts, against 102.90 to 102.93 GiB and 4,564,352 tokens in two starts of the bbad44fe cores.

    Limits of these numbers:

    The prototype overlapped prefill buffers inside the pools that 0572fd12 no longer has, so I have nothing left to propose here; from my side this can be closed.

  6. added
    triage:verify-for-closureMay already be fixed. Submitter: retest cited fix; close if resolved or report remaining symptoms.
    and removed
    triage:design-reviewNeeds a maintainer scope, architecture, or contract decision before implementation or integration.
    on Nov 13, 2026
  7. original-el8 commented on Nov 13, 2026

    @original-el8
    Collaborator

    Marked triage:verify-for-closure. The author's update confirms that compiler-planned temporaries replaced the manual scratch pools this report targeted. The measured fixed-memory reduction spans other changes too, so it should not be attributed solely to the scratch refactor.

    The original scratch proposal can be closed if no distinct remaining allocation problem is identified. The later default-export memory failure (#270) and transport-startup failure (#271) have their own tickets; their existence does not preserve the need for the removed scratch pools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:memoryMemory budgets, allocation lifetimes, scratch sharing, and capacity.area:preparationIgnition checkpoint loading, tuning, compilation, capture, and export.model:deepseek-v4.1Affected recipe or documented reproducer: deepseek-v4.1. Impact may be broader.performancePerformance, latency, throughput, or memory-efficiency investigation or improvement.triage:verify-for-closureMay already be fixed. Submitter: retest cited fix; close if resolved or report remaining symptoms.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions