[store] Avoid staging copy for same node tensor put - #3159
Conversation
zxpdemonio
left a comment
There was a problem hiding this comment.
Verdict
Useful same-process tensor write optimization with solid measured gains, but one behavioral bug should be fixed before merge, and the auto-enable of prefer_alloc_in_same_node needs clearer fallback semantics.
Blockers
-
MakeDefaultLocalTensorWriteConfigcannot honor an explicitprefer_alloc_in_same_node=False.
For an otherwise-defaultReplicateConfig, the helper always flips the flag totrue, even when the caller set it tofalse. There is currently no clean opt-out short of setting a dummy preferred segment / replica policy. That makes the new no-staging path mandatory for common tensor writes and contradicts the PR claim that unsupported paths keep the staging fallback. -
Default tensor put/upsert semantics change without a remote/staging fallback.
Defaultput_tensor/upsert_tensor/ batch variants now force same-node allocation + direct buffers. If the local segment cannot allocate, the write fails hard (NO_AVAILABLE_HANDLE/ same-process submit failure) instead of falling back to the old staging + TransferEngine path. Please either:- keep auto-prefer but add an automatic staging fallback when local alloc/submit fails, or
- make auto-prefer opt-in (or respect explicit
False) and document the capacity/behavior change prominently.
Non-blocking
-
append_tensor_write_buffersusessizeof(TensorMetadata)while the staging path usesmetadata.header.data_offset. Today builders set them equal, but the no-staging path should usedata_offsetfor layout consistency and future-proofing. -
batch_upsert_from_multi_buffershas noDummyClientimplementation (base default returnsINVALID_PARAMS). Tensor APIs already reject dummy clients, so this is mostly API asymmetry vsbatch_put_from_multi_buffers, but worth closing if upsert multi-buffer is now a public surface. -
Test coverage is thin relative to the behavior change.
- Added
test_batch_pub_tensor_same_nodeonly covers explicitprefer_alloc_in_same_node=True. - Missing coverage for: default config auto-prefer, explicit
Falseopt-out, upsert no-staging, and CUDA tensor correctness under the new default path (beyond the small get equality assert).
- Added
-
require_same_processintentionally bypassesMC_STORE_MEMCPY=0. That matches the benchmark story, but please call it out in docs so operators who disabled memcpy are not surprised.
What looks good
- Constraining no-staging to the current client segment via
normalize_same_process_tensor_write_config. - Forcing memcpy through
submit_batch(..., require_same_process=true)with local-endpoint checks. - Enabling upsert on the prefer-same-node path and fixing
FinalizeBatchUpsertto use allocated-replica transfer success instead ofpending_transfers(which are only tracked on merged ops). - Sharing put/upsert through
batch_*_from_multi_buffers/BatchWriteFromMultiBuffers. - Accelerator-aware memcpy worker already handles GPU to CPU for the direct path.
CI
Checks were still pending/skipped at review time; please confirm format + the listed tensor tests remain green on the latest head.
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
2746724 to
28aff51
Compare
00230d8 to
3837ec0
Compare
|
@zxpdemonio It seems that this PR has conflicts that must be resolved. |
16c8dc3 to
90bb26b
Compare
@yokinoshitayoki The rebase conflict has been resolved. The conflict was caused by the CUDA IPC support that recently landed upstream. After rebasing on top of it, we wired the dummy tensor write path to use the CUDA IPC handoff as well. With this change, same-node dummy-client GPU tensor writes can avoid the dummy/client local staging buffer: the dummy client exports the CUDA tensor via CUDA IPC, and the real client maps that payload and writes it directly into the local Store segment. I also added CUDA coverage for dummy upsert_tensor and verified the path with client metrics showing batch_upsert_from_cuda_ipc. |
|
Should we take a look at the failed CI tests? |
|
Thanks for the reminder. I checked the failed jobs. They were caused by EPEL mirror metadata download errors (404s and timeouts), not by the PR code. I reran the failed jobs, and the full workflow is now green: https://github.com/kvcache-ai/Mooncake/actions/runs/31068240815. No source changes were needed. |
|
Update summary for the latest head (
The current Linux workflow, including format, builds, wheel tests, and |
|
@zxpdemonio Seems this branch has conflicts that must be resolved, could you take a look? |
|
Supplemental DummyClient tensor-path validation The existing performance tables in the PR description use a real client. This is an additional end-to-end measurement for the tensor + DummyClient path. Environment:
Each write was followed outside the timed region by a DummyClient CUDA readback and payload comparison. The real-client log also confirmed that the 4 GiB Store segment was successfully registered as pinned memory. The 256 and 512 MiB tensors both exceed the 64 MiB client local buffer and still complete, confirming that the payload does not depend on a full-tensor client staging buffer. |
a85ec52 to
a0d187c
Compare
Prefer the current client segment for write-from paths when callers do not provide an explicit segment, keeping same-node tensor writes on the local memcpy path. Route tensor upsert through the PyClient interface and add the missing dummy multi-buffer upsert RPC plumbing so dummy and real clients share the same write API surface. Verified with code_format.sh, store/mooncake_client build, related C++ tests, and same-node CUDA real/dummy tensor correctness/performance checks.
a0d187c to
2239ce0
Compare
|
Rebased this PR onto the latest main, including the changes from #3197, and reran the same-node dummy-client CUDA tensor write benchmark. Configuration: MC_STORE_MEMCPY=1, 4 GiB pinned Store segment, 64 MiB client local buffer, CUDA device source. Every write was read back and verified.
The 256 MiB and 512 MiB tensors are larger than the 64 MiB client local buffer and still sustain about 25 GB/s, confirming that the same-node CUDA IPC/local-memcpy path remains no-staging after the rebase. |
Description
Motivation
The Python tensor write path used to copy tensor payloads through a client-side staging buffer before the data reached the Mooncake Store segment.
For local same-node tensor writes, this staging copy is unnecessary when the target Store segment is local to the writer process. It is especially expensive for GPU tensor -> Store CPU segment writes, where the old path first copied the GPU tensor into a client local/staging buffer and then copied that buffer into the Store segment.
This PR removes the extra staging copy from the local tensor write path.
This PR is split from the broader local tensor/offload optimization work. The scope here is intentionally limited to same-node tensor write staging removal.
Scenario
Before this PR:
After this PR, when the write can safely use the local same-node path:
Changes
put_tensorandupsert_tensorthrough the same no-staging multi-buffer path as batch tensor writes.Performance
Local write staging removal
Environment:
The benchmark log confirms
MC_STORE_MEMCPYwas auto-disabled under RDMA, so the baseline uses the old staging/TransferEngine path. The after case uses the same public tensor APIs and removes the local staging copy when the destination is the local Store segment.Local CPU tensor -> Store CPU segment put:
Local GPU tensor -> Store CPU segment put:
The main performance gain comes from local same-node tensor writes where the extra staging copy is removed.
Pinned Store segment memory local CUDA write
Environment:
This benchmark uses pinned Store segment memory and local memcpy. It validates that single tensor writes now reach the same no-staging multi-buffer path as batch tensor writes.
Local CUDA tensor -> pinned Store CPU segment
put_tensor:Local CUDA tensor -> pinned Store CPU segment
upsert_tensor:Module
mooncake-transfer-engine)mooncake-store)mooncake-ep)mooncake-pg)mooncake-integration)mooncake-wheel)mooncake-common)mooncake-rl)Type of Change
How Has This Been Tested?
Test commands:
./scripts/code_format.sh --check -b origin/main cmake --build build-tensor-staging-nocuda-notest -j8 --target store cmake -S . -B build-tensor-staging-cuda \ -DUSE_CUDA=ON \ -DWITH_STORE_RUST=OFF \ -DWITH_EP=OFF \ -DBUILD_UNIT_TESTS=OFF \ -DBUILD_TESTS=OFF \ -DCMAKE_BUILD_TYPE=Release cmake --build build-tensor-staging-cuda -j8 --target store mooncake_masterManual validation was run with local benchmark/smoke harnesses that are not included in this PR:
put_tensor,upsert_tensor,batch_put_tensor, andbatch_upsert_tensorput_tensorsmoke to confirm the existing successful return behavior for an existing keyTest results:
put_tensorsmoke passesManual test results:
put_tensorimproved from 3.33-3.36 GB/s to 24.92-25.92 GB/s.upsert_tensorimproved from 3.33-3.38 GB/s to 24.70-25.92 GB/s.Checklist
./scripts/code_format.shpre-commit run --all-filesand all hooks passAI Assistance Disclosure