Repository navigation
Enable asynchronous adaptive FP16/BF16 MoE expert swaps - #33155
Xavier Dupré (xadupre) wants to merge 28 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Multi-stream CUDA synchronization, callbacks, and mutable expert placement warrant final human review despite the focused tests.
Review effort: Balanced
Findings: None
What changed in this PR
Adds adaptive asynchronous expert swapping to FP16 CUDA MoE CPU offload. No actionable issues were identified.
Changes:
- Adds session-wide swap policy with epsilon thresholds and two concurrent exchanges per device.
- Implements provider-side asynchronous D2H/H2D staging and deferred publication.
- Adds configuration, documentation, policy tests, and CUDA integration coverage.
| File | Description |
|---|---|
onnxruntime/test/framework/ort_model_only_test.cc |
Covers minimal-build rejection. |
onnxruntime/test/framework/moe_expert_counting_test.cc |
Tests end-to-end swap publication and results. |
onnxruntime/test/framework/kernel_pilot_moe_expert_state_test.cc |
Tests policy, limits, and validation. |
onnxruntime/core/session/inference_session.cc |
Validates configuration and schedules only after successful runs. |
onnxruntime/core/framework/session_state.cc |
Initializes the swap threshold. |
onnxruntime/core/framework/kernel_pilot.h |
Exposes cache attachment and placement publication. |
onnxruntime/core/framework/kernel_pilot.cc |
Retains registered expert counts. |
onnxruntime/core/framework/kernel_pilot_moe_expert_state.h |
Declares adaptive policy state. |
onnxruntime/core/framework/kernel_pilot_moe_expert_state.cc |
Ranks experts and schedules bounded swaps. |
onnxruntime/core/framework/kernel_pilot_moe_expert_cache.h |
Defines the provider cache interface. |
onnxruntime/contrib_ops/cuda/moe/moe.h |
Declares CUDA swap resources and phases. |
onnxruntime/contrib_ops/cuda/moe/moe.cc |
Implements asynchronous transfer and publication. |
include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h |
Adds the epsilon configuration key. |
docs/moe-cpu-offload/README.md |
Documents adaptive swapping and remaining work. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…o moe-fp16-async-expert-swap
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avoid Hopper's automatic cuDNN SDPA path so MoE benchmark runs remain independent of cuDNN planner support for the model shape. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adopt alpha-only expert counters and keep async swap configuration and tooling aligned. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Expose the per-device in-flight swap limit as a session option and prompt-runner argument while preserving the default of four. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a configurable blend between binary expert selection and routing probability for adaptive CPU offload counters. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the asynchronous branch's structured selected-expert logging while incorporating the base branch fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use alpha 0.75 and epsilon 0.01 by default for adaptive expert placement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Report in-flight swaps and eligible candidates blocked by the per-device concurrency limit after each successful CPU-offload run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use compact CPU expert slots and exchange ownership with CUDA slots during adaptive swaps instead of retaining a full canonical host copy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Include node and expert identities with scores for every newly scheduled CPU/CUDA exchange so traces can correlate placement changes with inference iterations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Profiling update for dynamic offload with 1,000 CPU-resident experts on H200 (
The transfer engines spend only 0.60 ms/token on copies (1.6% of profiled latency). Incremental transfer activity accounts for roughly 2.2% of the regression. GPU GEMM time is unchanged, so neither PCIe bandwidth nor CUDA GEMM execution explains the slowdown. The offload path currently performs approximately, per decoded token:
MLAS GEMM represents about 16.5 ms of cumulative CPU-core work per token across worker threads. Sampling estimates roughly 1.1 ms/token on the critical thread, but Nsight Systems cannot give an exact wall-clock MLAS duration without additional internal instrumentation. Conclusion: the dominant issue is CPU/GPU orchestration and dependency bubbles, not transfer bandwidth or GPU GEMM. The extra 15.35 ms visible in the final stream synchronization represents unfinished work accumulated by the offload pipeline; it is not the intrinsic overhead of the synchronization call itself. The activity categories overlap and should not be added as independent latency components. The highest-priority optimization is therefore to keep routing counters and hot/cold selection on GPU, compact only CPU-routed rows, notify CPU workers asynchronously instead of synchronizing every MoE node, and wait only when CPU and GPU outputs must be combined. |
Transpose swap weights on CUDA instead of a blocking host callback. Add GPU counter snapshots and compact CPU workers while preserving adaptive placement, weighted counters and failure propagation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The adaptive implementation already contains the static counter and worker changes, adapted to compact CPU expert storage and weighted usage. Preserve that validated implementation while updating the stacked PR ancestry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Superseded by #33347, a self-contained main-targeted PR combining static CPU offload and adaptive expert swaps. It documents the actual shared-layout transfer design, the rejected bidirectional-layout experiment and all 33 available in-grid benchmark runs with actual repetition counts. Remaining measurements were stopped as requested. This PR is closed as superseded; its changes are included in the replacement branch. |
Description
Adds adaptive FP16/BF16 CUDA MoE expert swaps on top of #33039. After each successful inference, the session-global pilot ranks the hottest CPU expert against the coldest CUDA expert in each eligible node and schedules an exchange only when
cpu_max > (1 + epsilon) * cuda_min. Each kernel permits one pending exchange, and the scheduler permits at most four concurrent exchanges per CUDA device.The swap protocol keeps the published placement valid throughout the transfer:
StartSwap()waits for the outgoing expert's last use on the compute stream. CUDA kernels transpose its FC1/FC2 weights into MLAS layout before a dedicated D2H stream copies them, along with biases, into reusable outgoing pinned storage.KernelPilot.This delayed-publication protocol ensures that an invocation sees either the complete old placement or the complete new placement, never partially transferred weights. Up to four kernels per CUDA device can pipeline independent D2H and H2D transfers; hardware with fewer copy engines serializes them without changing correctness. Per-node slot counts and the session-global CUDA expert budget remain unchanged; cross-node slot redistribution and dynamic adjustment of the concurrency limit remain follow-up work.
Validation:
cmake --build build/linux_cuda/Release --target onnxruntime_test_all onnxruntime_provider_test -j 8— passedCUDA_VISIBLE_DEVICES=5 build/linux_cuda/Release/onnxruntime_test_all --gtest_filter='MoeExpertCountingTest.AdaptiveCpuOffloadPublishesCompleted*SwapsAtNextInvocation:KernelPilotMoeExpertStateTest.*'— 17 passed, including real FP16 and BF16 delayed-publication swapsCUDA_VISIBLE_DEVICES=5 build/linux_cuda/Release/onnxruntime_provider_test --gtest_filter='MoETest.MoECuda*16StaticCpuOffloadRunsDisabledMixedAndAllCpuExpertsAndCountsUsage'— 2 passed, covering FP16 and BF16 disabled, mixed, and all-CPU placementcmake --build build/linux_moe_minimal/MinSizeRel --target onnxruntime_test_all -j 8— passedbuild/linux_moe_minimal/MinSizeRel/onnxruntime_test_all --gtest_filter=OrtModelOnlyTests.RejectsMoeExpertCountingInMinimalBuild— 1 passedlintrunner -a— passedLatest transfer and execution update:
Matched unprofiled performance, Qwen3.5-35B FP16, GPU 5, 1,000 CPU experts:
The final row measures prompts 3–5 from the same five-prompt runner, 256 generated tokens per prompt, alpha 0.9, epsilon 0.1, swaps enabled, counter logging disabled and CUDA Graph disabled. Prefill and the first generation call without an actual decode inference are excluded: 255 actual decode runs per scored prompt, 765 total. Per-prompt means are 18.718456, 16.516421 and 19.388907 ms/token. The measured gain over the historical baseline is 7.830724 ms/token, not yet the requested 10 ms.
Latest validation:
cmake --build build/linux_cuda/Release --target onnxruntime onnxruntime_test_all onnxruntime_provider_test onnxruntime_providers_cuda_ut -j 16— passed.cd build/linux_cuda/Release && CUDA_VISIBLE_DEVICES=5 timeout 180 ./onnxruntime_test_all --gtest_filter='KernelPilotMoeExpertStateTest.*:MoeExpertCountingTest.*'— 49 passed, none skipped.cd build/linux_cuda/Release && CUDA_VISIBLE_DEVICES=5 timeout 180 ./onnxruntime_provider_test --gtest_filter='MoETest.MoECuda*AdaptiveSwap*:MoETest.MoECuda*StaticCpuOffload*'— 13 passed, including repeated FP16/BF16 swaps with asymmetric weights and biases.cd build/linux_cuda/Release && CUDA_VISIBLE_DEVICES=5 GTEST_FILTER='KernelPilotMoeExpertSelectionCudaTest.*' timeout 180 ./onnxruntime_provider_test --gtest_filter='CUDA_EP_Unittest.All'— 8 internal CUDA tests passed, including actual v2 stream-memory completion.cd build/linux_cuda/Release && CUDA_VISIBLE_DEVICES=5 timeout 120 ./onnxruntime_test_all --gtest_filter='MoeExpertCountingTest.StaticCpuOffloadKeepsBranchLocalWeightsSeparateFromAncestor' --gtest_repeat=30— all 30 repetitions passed after fixing the upload dependency.Motivation and Context
Static FP16/BF16 placement from #33039 cannot adapt when routing popularity changes between requests. Asynchronous swaps improve placement over time without making the next MoE invocation wait for expert-weight transfers or invalidating the mapping used by in-flight CUDA work.