Conversation
After its device instance drains, each RDMA transfer worker keeps busy-polling for a hard-coded 100 ms before it parks on the pool's condition variable. A polling worker holds a whole CPU core, and every worker of an instance polls while any slice on it is in flight. When transfers arrive more often than every 100 ms but are far from saturating the NICs, the workers never park. On an 8-GPU decode node pulling KV cache over 8 HCAs with MC_WORKERS_PER_CTX=4 (256 workers in 8 processes), they kept about 90 cores busy while the actual post and poll work needed a small fraction of that, and exhausted the container's CPU quota. Add MC_RDMA_WORKER_IDLE_SPIN_US (default 100000, i.e. the current 100 ms; range 0-10000000) to set this window. 0 parks a worker as soon as its instance is idle. Parking keeps the existing protocol: the double check under cond_mutex_, notify_all from submitters while a worker is parked, and the 1 s wait_for timeout as a backstop. A worker still polls while it has outstanding completions. Invalid values keep the default, as with MC_CONN_PAUSE_TTL_MS, and an accepted value is logged. The UB transport has the same constant but a different idle loop and is left unchanged. Testing: - config_test (config.cpp and tests/config_test.cpp) built with GCC 13.1, glog 0.7.1 and googletest 1.15.2: 117/117 pass, including the nine new RdmaWorkerIdleSpinEnvTest cases. - worker_pool.cpp compiles cleanly with -std=c++20 -Wall -Wextra. - scripts/code_format.sh --check --changed-lines and pre-commit on the changed files pass; the docs HTML build has no warnings. - Not run: the full CMake build, RDMA integration tests, or measurements on RDMA hardware. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
changcui
requested review from
ShangmingCai,
UNIDY2002,
alogfans,
chestnut-Q,
doujiang24,
staryxchen,
stmatengss and
ykwd
as code owners
September 25, 2026 09:18
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
WorkerPool::transferWorkerbusy-polls for a hard-coded 100 ms (kWaitPeriodInNano) after its device instance becomes idle, and only then parks on the pool's condition variable. The idle check is pool-wide (processed_slice_count_ == submitted_slice_count_). As a result, every worker of an instance keeps polling while any slice on that instance is in flight, and each polling worker occupies a whole CPU core.When transfers arrive more often than every 100 ms but are far from saturating the NICs, the workers never park. We hit this in a PD-disaggregated deployment. An 8-GPU decode node pulled KV cache over 8 HCAs with
MC_WORKERS_PER_CTX=4, which is 256 workers across 8 processes. Those workers kept about 90 cores busy, although the actual post and poll work needed only a small fraction of that. The node exceeded its container CPU quota, and CFS throttling then stalled the inference processes.This PR adds
MC_RDMA_WORKER_IDLE_SPIN_USto set the window:100000(100 ms), so behavior is unchanged unless the variable is set.0-10000000.0parks a worker as soon as its instance is idle.MC_CONN_PAUSE_TTL_MS. Non-numeric, negative, out-of-range or suffixed values are ignored with a WARNING and the default is kept. An accepted value is logged at INFO.cond_mutex_, thenotify_allfrom submitters whileparked_worker_count_ > 0, and the 1 swait_forbackstop. A worker with outstanding completions still polls.ub_context.cpp) has the same constant but a different idle loop, and is left unchanged.Trade-off: with a shorter window, a parked worker pays a thread wake-up when the next transfer arrives. Deployments that care most about latency and have spare cores can keep the default.
Module
mooncake-transfer-engine)mooncake-store)mooncake-conductor)mooncake-reshard)mooncake-ep)mooncake-pg)mooncake-integration)mooncake-p2p-store)mooncake-wheel)mooncake-common)mooncake-rl)Type of Change
How Has This Been Tested?
Test commands:
Test results:
config_test117/117, including the 9 newRdmaWorkerIdleSpinEnvTestcases.worker_pool.cppcompiles with no warnings. The format check and the pre-commit hooks pass, and the docs HTML build has no warnings. The change has not yet been measured on RDMA hardware.Checklist
./scripts/code_format.shAI Assistance Disclosure
Claude Code (Claude Opus 5.5) helped trace the CPU usage to this idle spin, drafted the code change, tests, docs and this description, and ran the checks listed above.
🤖 Generated with Claude Code