mirror of
https://github.com/kvcache-ai/Mooncake.git
synced 2026-11-05 01:17:55 +00:00
[GH-ISSUE #4242] [RFC]: Generalize adaptive scatter transfer for Engram, Reshard, and Structured Object #1063
Labels
No labels
ascend
auto-closed
bug
bug
CI/Build
Common
documentation
enhancement
feature request
good first issue
help wanted
high priority
MetaX
nightly-failure
pull-request
PyTorch Backend
question
RFC
Roadmap
stale
Store
Tests
Transfer Engine
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
kvcache-ai/Mooncake#1063
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @zxpdemonio on GitHub (Sep 20, 2026).
Original GitHub issue: https://github.com/kvcache-ai/Mooncake/issues/4242
Changes proposed
This RFC proposes a common adaptive scatter-transfer path for three Mooncake
workloads:
The common layer should decide between direct RDMA and owner-side gather, use
pre-registered staging memory when gathering is profitable, and overlap packing
with bulk RDMA under bounded resource limits. The policy belongs in Transfer
Engine and Store transfer primitives rather than in separate Engram, Reshard,
or Structured Object implementations.
PR #4117 is the first implementation step. It adds an adaptive gather path for
many-small scatter READs while retaining the existing direct path and fallback.
This RFC documents the workload model, current bottlenecks, performance goals,
and the parts of the Structured Object design that should become reusable
infrastructure.
Motivation
Classic Transfer Engine is efficient when a request contains a small number of
large registered-memory ranges. It is less efficient when one logical operation
contains tens of thousands of small fragments. Posting one RDMA operation per
fragment exposes WQE, doorbell, CQ, queue-depth, and completion costs instead of
the link bandwidth.
Gathering small source fragments into registered staging memory replaces many
small RDMA operations with a few bulk operations, but it introduces different
costs:
The optimization therefore cannot be a fixed fragment-size threshold or a
larger global RDMA queue. It must compare the complete direct and gather costs
for the actual fragment geometry and available hardware resources.
Metric definition
Two bandwidth numbers must be reported separately:
post to final data completion. This measures whether the network is saturated.
transferScatter()or Store API latency. This includes planning, commandhandling, packing, transfer, and completion publication.
On two A10 + ERDMA peers, the current measurements are:
The owner-side bulk RDMA window for the 98,304 x 264 B workload is about
2.47 ms, or approximately 84 Gbit/s. The RNIC is therefore already close to
line rate after data has been packed. The 35 Gbit/s end-to-end result is not an
RDMA bandwidth regression; it is the cost of the complete many-small-fragment
pipeline.
Current bottlenecks
Profiling the 98,304 x 264 B workload gives the following approximate costs:
Additional experiments show that this is not solved by increasing parallelism:
More shards reduce initial packing time but add command handling, decoding,
staging contention, and RDMA scheduling overhead. Smaller chunks improve overlap
only until per-chunk submission and completion costs dominate. The current four
shards are near the best point for this machine, but the remaining requester and
CPU-gather costs are still too large.
Store integrations may add another upper-layer cost: lease refresh and
per-fragment result materialization. That cost must be profiled separately from
the Transfer Engine data window and should use batch completion wherever the API
contract permits it.
Workload scenarios
1. Engram
Engram reads many fixed-size embedding rows from one or a few large tables into
a contiguous output buffer. Source rows are usually random, destinations are
ordered, and individual fragments are small (264 B in the current DeepSeek
integration). The path is latency-sensitive because it contributes directly to
TTFT and may execute for multiple layers.
Relevant characteristics:
Engram should submit one logical layer read. It should not implement its own
gather policy, staging allocator, or shard scheduler.
2. Reshard
Reshard has a wider geometry. A plan may contain a few large contiguous regions,
many small intersections, or both in the same operation. Source and destination
layouts can differ because of TP, PP, DP, EP, head placement, or page layout.
Relevant characteristics:
Reshard therefore needs a hybrid plan, not one global mode. The planner should
partition a logical operation into direct ranges and gatherable runs, execute
both under one completion object, and never materialize the complete tensor only
to reshape it.
3. Structured Object
Structured Object stores metadata plus named tensor, ndarray, bytes, or ragged
members. Its PUT path may receive multiple source buffers that form one logical
payload. Its GET path may selectively materialize members or byte ranges into a
caller-provided destination.
Relevant characteristics:
Structured Object already implements useful mechanisms in PRs #3023, #3024,
and #3112. This RFC does not move manifests, codecs, or Python object semantics
into Transfer Engine. It generalizes the transport ideas underneath them.
Structured Object ideas generalized by this RFC
The following Structured Object ideas apply directly to Engram and Reshard:
Preserve scatter input until the staging boundary. Multi-buffer PUT avoids
building one large Python
bytesobject. Engram row IDs and Reshard logicalintersections should likewise remain compact descriptors until the owner
copies them into staging memory.
Use pre-registered BufferPool leases. Structured Object stages grouped
PUTs in reusable registered pool memory before calling
batch_put_from.PR #4117 extends the same ownership model to owner-side scatter READ gather:
acquire a bounded lease, pack into it, submit bulk RDMA, and release it after
completion.
Chunk work and overlap copy with transfer. Structured Object splits large
payloads into bounded chunks and permits multiple chunks in flight. The
generic scatter path uses the same pipeline shape for random rows and Reshard
fragments, with chunk size and depth selected from a cost model rather than
Structured Object's rollout-specific defaults.
Write/read directly from registered buffers. Structured Object uses
batch_put_from,batch_get_into,get_into_ranges, caller destinations,and pool-backed arrays to avoid intermediate Python-owned payloads. Engram
and Reshard should end in their final registered CPU/GPU destination whenever
the transport and layout permit it.
Batch one logical operation. Structured Object groups physical chunks
behind one logical object operation. Scatter transfer should similarly use
compact plans and batch completion instead of one control round trip and one
result publication per fragment.
Keep bounded fallback behavior. Structured Object falls back to ordinary
Store PUT/GET when BufferPool or fast-path APIs are unavailable. Generic
scatter must keep direct transfer as the correctness fallback when capability
negotiation, staging allocation, validation, or active gather fails.
Separate policy from data semantics. Structured Object retains member and
manifest semantics above its payload transport. Engram retains table/row
semantics, and Reshard retains logical placement semantics. Transfer Engine
should receive only registered ranges, ownership, and completion contracts.
The following parts stay Structured Object-specific and are not generalized:
manifest publication, codecs, cleanup keys, grouped object placement, Python
ownership wrappers, and fixed 64 MiB rollout defaults.
Proposed common design
Normalized scatter plan
Callers lower their domain-specific operation into registered source and
destination ranges. A single O(N) normalization pass should:
Adaptive direct, gather, and hybrid modes
For total bytes
S, fragment countN, coalesced span countM, link bandwidthB_net, measured packing bandwidthB_mem, small-copy rateR_copy, RDMA postrate
R_post, queue depthQ, RTTL, chunk sizeC, and pipeline widthP:The
C / B_memandC / B_netterms model pipeline fill and drain. Candidatechunk sizes should satisfy both bandwidth-delay-product and fixed-cost
amortization constraints:
The planner evaluates bounded
(mode, C, P)candidates and selects gather onlywhen it beats direct by a safety margin. A mixed request applies this decision
per compatible run, so a few large ranges remain direct while many small ranges
can share a gather pipeline.
B_net,Q, and transport limits come from active contexts and configuration.B_mem,R_copy, control latency, and fixed chunk cost should come from a smallstartup calibration or conservative moving measurements. The policy must not
contain NIC model names, token counts, or topology-specific constants.
Effective packing/transfer overlap
PR #4117 introduces bounded staging and pipeline depth, but profiling shows that
the current one-wave four-shard layout still pays most of the first large-chunk
packing cost before the link becomes busy. The follow-up should:
Performance goals
The primary goals are relative so they remain meaningful across RDMA NICs:
control throughput.
S >= 16 MiB), complete API usefulthroughput should reach at least 70% of the direct control throughput. On the
current reference peers this means at least 55 Gbit/s and no more than 3.8 ms
p50 for 98,304 x 264 B.
two network RTTs plus 20% of the local gather time.
better forced-direct or forced-gather result without caller tuning.
P * C; pool exhaustion must fall backwithout partial success or leaked leases.
These goals deliberately distinguish link saturation from full API latency.
Reaching 80-84 Gbit/s inside the RDMA window does not by itself satisfy the
Engram TTFT goal.
Integration boundaries
lookup_intolowers selected rows into one Store ranged read andsupplies the final registered destination.
mooncake-reshard; the executor lowersresolved edges into one generic scatter operation.
Rewiring a PUT or GET path to the generic scatter executor requires an A/B
benchmark against the current BufferPool and ranged-read implementation.
provisioning. Transfer Engine owns only transfer planning and execution.
staging lifecycle, transfer completion, and transport fallback.
Validation matrix
Validation must include cross-machine RDMA and local controls:
Every result should report payload, p50/p95, full API goodput, RDMA-window
goodput, requester planning, command/decode, packing, and completion time.
Phasing
compact commands, direct fallback, and focused tests.
command state where safe, and add low-overhead stage metrics.
workloads; calibrate the portable cost model.
and validate both correctness and the mixed-workload performance target.
migrate only paths that preserve or improve current performance.
destinations in vLLM/SGLang and report TTFT impact.
Non-goals
remote_accessible, or pinned-memorydefaults solely for these workloads.
Transfer Engine.
Related work
Before submitting a new issue...
@github-actions[bot] commented on GitHub (Sep 20, 2026):
Thanks for opening this issue, @zxpdemonio!
16206312A maintainer will triage this when possible. To help us respond faster, please include:
Useful links: Documentation · Contributing guide