Skip to content

Link received payload objects into the core cache instead of copying - #125

Open
javieratomic wants to merge 1 commit into
NVIDIA:mainfrom
javieratomic:receive-adopt
Open

javieratomic wants to merge 1 commit into
NVIDIA:mainfrom
javieratomic:receive-adopt

Conversation

@javieratomic

@javieratomic javieratomic commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Why

A rank export receives each cache payload object as a complete companion wrapper in a synced temporary file in the core cache. store_slice then hashes the received payload, copies it into a new wrapper in cache staging, syncs the copy, and hashes the installed copy again: three full reads and one write and sync per object, on the receiving stream's thread. When the received file already is the wrapper store_slice would write, the copy, its write and its sync repeat work whose result is already on disk. In a DeepSeek V4.1 Flash TP3 export on three DGX Spark GB10 hosts with an empty core cache, rank zero receives 203.5 GB from the other two ranks this way.

What changed

Before, the receiver always called store_slice. Now it first calls the new Root::adopt_companion, which links the received file itself into cache staging when it is byte-for-byte that wrapper. That removes the copy read, the write and the sync; both hashes remain.

The link step is shared with directory publication, which already hard-linked source files that are canonical wrappers from its own code in export.rs; that code moves into container::link_companion, which:

  • checks the length and leading bytes against companion_prefix;
  • links the opened file through its descriptor, so that a replaced path cannot be linked instead;
  • checks that the link names the same inode;
  • hashes the linked file against the declared identity. A mismatched identity is an error.

For a file that is not the canonical wrapper, or a link the filesystem refuses, link_companion returns false, adopt_companion returns None, and the receiver stores the slice as before. Adoption checks and links the descriptor the receiver opened, the same one store_slice copies from. An already cached identity returns the verified cached object, as store_slice does. Publication is unchanged: the object is made immutable, linked under its identity and verified once more, and the receiver closes its writable descriptor before the file is published.

Core format, cache layout and object identities are unchanged. Directory publication keeps its behavior, error kinds and messages. Adoption needs Linux (linkat through /proc/self/fd); on other platforms received objects are copied as before.

Result

Expected: with an empty core cache, "Receive rank export payloads" no longer copies, writes and syncs each received object. Measured on an earlier tree with an earlier version of this adoption (below), that phase took 280.6 s instead of 457.8 s. This PR has not been measured on its current base.

Evidence

Tests on this branch:

  • helios cargo test --release --features server,runtime-core --lib, with FLARE_TEST_TEMPLATE_DIR prepared by tests/frontend/prepare_templates.py: 1335 passed, 0 failed, 26 ignored.
  • heliopause cargo test --release: 190 passed, 0 failed, 3 ignored.
  • A helios test covers:
    • adoption by link (same inode);
    • a wrapper with an extra tensor and a wrapper of the right length with different header bytes, both declined;
    • a mismatched identity, which is rejected;
    • a second arrival of a cached identity, which returns the cached object;
    • that cache staging is empty after the declined, rejected and cached cases.
  • A heliopause test checks that the receiver's install step adopts a canonical wrapper (same inode) and copies another framing into a new file.
  • The existing directory publication tests cover the shared link step.

The 203.5 GB is the byte count reported by "Receive rank export payloads" in an export on d2b5c5f3 without this change, with an empty core cache; there that phase took 462.3 s.

Measurement on an earlier tree: full TP3 exports on three DGX Spark GB10 hosts with the same model and export command, an empty core cache (then named the package cache) and an empty forward compilation cache. The tree was ca1f960e plus an earlier version of #124 that published cache payloads concurrently, without the directory publication changes merged as e13ecb74. Its adoption step linked the received file by path instead of through the shared link_companion. Phase names are as reported then.

without adoption with adoption
Receive rank export payloads 457.8 s (8 streams), 477.2 s (--transfer-streams 16) 280.6 s (8 streams)
Export native packages 1677.4 s 1473.2 s

The package exported with adoption served with flare run. It answered a 262k-token prompt that asks for 16 values placed through the context with all 16 correct, with identical output with an empty and with a warm prefix cache.

Limitations:

  • One export per configuration was measured, on a different tree and with a different link step.
  • The payload is still read twice after it arrives (identity check and post-installation verification) instead of being hashed while it streams.
  • Publish core cache payloads on a largest-first work queue and read ahead in slice digests #124 also changes hashing in this phase: its read-ahead applies to Slice::digest of payloads of at least 32 MiB while fewer than 16 digests are reading ahead, which both store_slice and adoption use for the identity check. The two changes were not measured together.

🤖 Generated with Claude Code

@javieratomic
javieratomic force-pushed the receive-adopt branch 2 times, most recently from 610bc96 to b181fc8 Compare October 30, 2026 09:51
@original-el8 original-el8 added area:caching Prefix/KV reuse, cache retention, and model or payload cache behavior. area:packaging Package assembly, verification, distribution, inspection, and loading. performance Performance, latency, throughput, or memory-efficiency investigation or improvement. triage:review Proposed change needs code review; this does not imply merge readiness. labels Nov 5, 2026
@original-el8 original-el8 added the model:deepseek-v4.1 Affected recipe or documented reproducer: deepseek-v4.1. Impact may be broader. label Nov 8, 2026
A rank export receives each cache payload object as a complete companion
wrapper in a synced temporary file in the core cache. `store_slice` then
hashes the received payload, copies it into a new wrapper in cache
staging, syncs the copy, and hashes the installed copy again: three full
reads and one write and sync per object, on the receiving stream's
thread. When the received file already is the wrapper `store_slice`
would write, the copy, its write and its sync repeat work whose result
is already on disk. In a DeepSeek V4.1 Flash TP3 export on three DGX
Spark GB10 hosts with an empty core cache, rank zero receives 203.5 GB
from the other two ranks this way.

Before this change the receiver always called `store_slice`. Now it
first calls the new `Root::adopt_companion`, which links the received
file itself into cache staging when it is byte-for-byte that wrapper;
both hashes remain. The link step is shared with directory publication,
which already hard-linked source files that are canonical wrappers from
its own code in `export.rs`; that code moves into
`container::link_companion`. It checks the length and leading bytes
against `companion_prefix`, links the opened file through its descriptor
so that a replaced path cannot be linked instead, checks that the link
names the same inode, and hashes the linked file against the declared
identity; a mismatched identity is an error. For a file that is not the
canonical wrapper, or a link the filesystem refuses, `link_companion`
returns false, `adopt_companion` returns `None`, and the receiver stores
the slice as before. Adoption checks and links the descriptor the
receiver opened, the same one `store_slice` copies from. An already
cached identity returns the verified cached object, as `store_slice`
does. Publication is unchanged: the object is made immutable, linked
under its identity and verified once more, and the receiver closes its
writable descriptor before the file is published. Core format, cache
layout and object identities are unchanged, and directory publication
keeps its behavior, error kinds and messages. Adoption needs Linux
(`linkat` through `/proc/self/fd`); on other platforms received objects
are copied as before.

Expected result: with an empty core cache, "Receive rank export
payloads" no longer copies, writes and syncs each received object. On an
earlier tree, with an earlier version of this adoption, that phase took
280.6 s instead of 457.8 s; this commit itself was not measured on its
current base.

Evidence: helios `cargo test --release --features server,runtime-core
--lib`: 1335 passed, 0 failed, 26 ignored, with FLARE_TEST_TEMPLATE_DIR
prepared by tests/frontend/prepare_templates.py. heliopause `cargo test
--release`: 190 passed, 0 failed, 3 ignored. A helios test covers
adoption by link (same inode); a wrapper with an extra tensor and a
wrapper of the right length with different header bytes, both declined;
a mismatched identity, which is rejected; a second arrival of a cached
identity, which returns the cached object; and that cache staging is
empty after the declined, rejected and cached cases. A heliopause test
checks that the receiver's install step adopts a canonical wrapper (same
inode) and copies another framing into a new file. The existing
directory publication tests cover the shared link step. The 203.5 GB is
the byte count reported by "Receive rank export payloads" in an export
on `d2b5c5f3` without this change, with an empty core cache; there that
phase took 462.3 s. The measurement above used full TP3 exports with the
same model and export command, an empty core cache (then named the
package cache) and an empty forward compilation cache, built from
`ca1f960e` plus an earlier version of NVIDIA#124 that published cache payloads
concurrently and without the directory publication changes merged as
`e13ecb74`; its adoption step linked the received file by path instead
of through `link_companion`. There, with the default 8 transfer streams,
"Receive rank export payloads" took 280.6 s with adoption and 457.8 s
without it (477.2 s without it and `--transfer-streams 16`), and the
whole export phase took 1473.2 s against 1677.4 s; the package exported
with adoption served with `flare run` and answered a 262k-token prompt
that asks for 16 values placed through the context with all 16 correct,
with identical output with an empty and with a warm prefix cache. One
export per configuration was measured. The payload is still read twice
after it arrives (identity check and post-installation verification)
instead of being hashed while it streams.

Design-Signed-Off-By: Claude Opus 5.5; design-review=cfcf
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com>
@javieratomic javieratomic changed the title Link received payload objects into the package cache instead of copying Link received payload objects into the core cache instead of copying Nov 10, 2026
@javieratomic

Copy link
Copy Markdown
Contributor Author

Rebased onto bbad44fe. The code is unchanged apart from the package-to-core rename. Directory publication's link step, which this PR moves into container::link_companion, is identical on the old and new base, and on both it already runs on up to 8 concurrent installation workers; link_companion keeps no shared state. The cache payload checks that af0907d9 made concurrent do not use the link step. The branch merges cleanly with the #124 head 50c664a3. On the rebased branch: helios cargo test --release --features server,runtime-core --lib: 1335 passed, 0 failed, 26 ignored; heliopause cargo test --release: 190 passed, 0 failed, 3 ignored. The received-bytes figure (203.5 GB, previously 203.7 GB) now comes from an export on d2b5c5f3 without this PR, in which "Receive rank export payloads" took 462.3 s. The adoption measurement is still the earlier one on a different tree; this PR has not been measured on its current base.

javieratomic added a commit to javieratomic/hyperflare-inference that referenced this pull request Nov 10, 2026
Name the upstream project and repository after their rename, point every
link at NVIDIA/hyperflare-inference, note that NVIDIA#124 and NVIDIA#125 were rebased
onto upstream's package-to-core rename, add the NVIDIA#124 re-measurement, and
say where the prefill-scratch prototype's branches are.

Design-Signed-Off-By: Claude Opus 5.5; design-review=1c01
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com>
javieratomic added a commit to javieratomic/hyperflare-inference that referenced this pull request Nov 10, 2026
When a core is assembled, `core_engram` resolves each Engram plane's
payload source in turn, and `payload_source` hashes each plane, or
copies and hashes it when it spans several files, with one synchronous
reader. On DeepSeek V4.1 Flash the two weight planes are about 98 GB
each, so "Assemble core payloads" spends most of its time hashing one
plane at a time while one reader at a time cannot keep the NVMe device
busy.

Before this change `core_engram` validated each table and resolved its
planes one after another. Now it first validates every table and lists
its planes (shared weights and scales, or each shard's weights and
scales) with the same checks as before; the new `plane_sources` then
resolves those planes with `payload_source` on up to eight workers,
largest first, stops scheduling at the first error and returns it once
running planes finish. The sources come back in declared order, and the
tables, shards and installed payloads are built from them in the same
order as before, so the published layout does not change.

Measured result, on an earlier base: on a TP3 export on three DGX Spark
GB10 hosts, "Assemble package payloads" (now "Assemble core payloads")
took 169.1 s instead of 262.0 s.

Evidence: helios `cargo test --release --features server,runtime-core
--lib`: 1334 passed, 0 failed, 26 ignored. heliopause `cargo test
--release`: 190 passed, 0 failed, 3 ignored, including
`core_engram_preserves_shared_and_partitioned_plane_layouts`. The new
test `plane_sources_keep_declared_order_and_report_failures` resolves
three planes of different sizes, one of them spanning two source files,
checks that they come back in declared order with the right identities
and sizes, and checks that a plane beyond the payload fails. The
measurement used full exports with the same model and recipe flags, one
per configuration, on builds based on `a161b17f` that also contained
earlier versions of NVIDIA#122, NVIDIA#124 and NVIDIA#125 and the local checkpoint reading
proposed in NVIDIA#126, none of which change this phase in those versions; the
export completed and committed the core, including the data of all three
ranks. This model has four planes, so four readers run at once; the
other two planes are about 3 GB each, so most of the remaining time is
the two weight planes, each still hashed by one reader. The change was
not measured on `bbad44fe`, nor together with the read-ahead digest of
NVIDIA#124 (`50c664a3`), which also shortens this phase.

Design-Signed-Off-By: Claude Opus 5.5; design-review=cfcf
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:caching Prefix/KV reuse, cache retention, and model or payload cache behavior. area:packaging Package assembly, verification, distribution, inspection, and loading. model:deepseek-v4.1 Affected recipe or documented reproducer: deepseek-v4.1. Impact may be broader. performance Performance, latency, throughput, or memory-efficiency investigation or improvement. triage:review Proposed change needs code review; this does not imply merge readiness.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants