Repository navigation
MoE hot experts: one batched device call per layer - #38
Merged
Merged
Conversation
Contributor
Author
|
RNGD data for this design. I implemented the same approach independently on a branch (stacked hot weights + bmm, one call per layer and direction, padded buckets) before seeing this PR, and ran it on RNGD. It is close enough to this PR that the numbers should carry over; I'm dropping my branch and adding them here. Setup: Qwen3-30B-A3B-Instruct-2507 LoRA, hot 25% (K = 32 per layer), seq 128, RNGD chip 0, furiosa-torch 2026.2.0. The host was shared (load average 4–43 from other users' jobs).
*
|
gspark-etri
force-pushed
the
perf/35-batch-hot-experts
branch
2 times, most recently
from
October 21, 2026 10:53
961f4ec to
4057701
Compare
Pack the rows routed to a layer's hot experts into [E_hot, bucket, H] and run the experts as bmm over stacked weights [E_hot, 2F, H] / [E_hot, H, F] kept resident on the device per layer. Cold experts stay on the host. Adds test_hot_expert_packing; the hot-expert test now checks the call count (3 per layer, not per expert). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tats Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gspark-etri
force-pushed
the
perf/35-batch-hot-experts
branch
from
October 21, 2026 12:58
4057701 to
f1419e4
Compare
gspark-etri
marked this pull request as ready for review
October 21, 2026 12:58
This was referenced Oct 21, 2026
gspark-etri
added a commit
that referenced
this pull request
Oct 21, 2026
…ost-only on RNGD (#44) ## Background After #38, hot experts on RNGD were at parity with host-only: 13.0 vs 13.5 s/step, alternating steps at host load 120–150. #35 is still open for "faster than host-only". A per-step profile (Qwen3-30B-A3B, LoRA r16 q·v, S=128, hot fraction 0.25) shows where the time went: `HostMLP.backward` recomputes the whole MoE block forward before its autograd pass. That is about a third of the host MoE time, and one of the three device calls per layer. ## Changes - `npu_lora/moe.py`: `HostMLP(keep_graph=True)`. `forward` builds the autograd graph and keeps `(x, y)` per layer. `backward` reuses it when it receives the same input tensor, and otherwise recomputes as before. It costs the block's saved activations until that layer's backward, a few MB per layer at S=128. - Tests: - `test_host_mlp_keep_graph_equals_recompute`: LoRA input gradients and full-FT parameter gradients are bitwise equal to the recompute path; - the hot-expert test now expects 2 device calls per layer instead of 3. - Record: a follow-up section in `experiments/2026-10-21-moe-batched-hot-experts.md`, with raw data and the profiler in `experiments/data/moe-batched/`. STATUS updated. ## Verification CPU: `python -m pytest -q tests` (27 passed, torch 2.14.1+cpu, transformers 5.20.2). RNGD setup: - hardware: npu-node-1, one card in a run-and-exit k8s Job; furiosa-torch 2026.2.0 / furiosa-llm 2026.3.0, torch 2.10.0+cpu, transformers 5.1.0; - model and data: Qwen3-30B-A3B-Instruct-2507, NSMC data, seed 0, hot set fixed after 4 host-only warm-up steps; - method: one process; 6 rounds of 4 configurations, order reversed every other round; load average 53; - commit: `1fee9f2`. | config (median step) | step | attention + head (device) | HostMLP fwd | HostMLP bwd (of which recompute) | |---|---:|---:|---:|---:| | host-only (as `main`) | 9.21 s | 1.26 | 2.48 | 5.34 (2.48) | | host-only + keep_graph | 8.00 s | 1.16 | 2.46 | 4.31 (0) | | hot (as #38) | 6.70 s | 1.07 | 1.73 | 3.79 (1.66) | | **hot + keep_graph** | **5.19 s** | 1.10 | 1.81 | 2.21 (0) | Paired per round: - hot + keep_graph minus host-only + keep_graph: **median −2.66 s (−33%)**, 6 of 6 rounds negative; - hot + keep_graph minus host-only as on `main`: median −4.17 s (−45%). Reproduce: `python3 -m npu_lora.moe_prof MODEL DATA 0.25 128 6 out.jsonl`, with `experiments/data/moe-batched/moe_prof.py` copied into `npu_lora/`. ## Compatibility and limits - `keep_graph` is on by default. It speeds up host-only MoE too (9.21 → 8.00 s). Pass `keep_graph=False` to get the old memory profile. - The gain depends on the shared host's load. At load 120–150 (#38's A/B) hot and host-only were at parity without `keep_graph`; at load 53, hot was already faster. - Not tried: E=64 and S=256/512. Each needs about 40 min of compile per expert bucket, and S needs new attention graphs. They were not needed for the criterion. ## Related Issue Closes #35 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Background
With hot experts on RNGD (#27), Qwen3-30B-A3B LoRA ran about 615 s/step against about 195 s/step host-only. Each hot expert was its own device call, with its own upload and download (#35).
Changes
npu_lora/moe_device.py: a layer's hot experts now run as one device call per direction.[E_hot, bucket, H]. The bucket is the busiest hot expert's row count, rounded up to 8…1024.experts_fwd/experts_bwd_fnare bmm over stacked weights[E_hot, 2F, H]/[E_hot, H, F]. Graphs compile once per(E_hot, H, F, bucket).replan) or after a full-FT update (refresh).gate_up_proj[hot]carry the weight gradients back through autograd. LoRA passes no host stacks, which avoids a 300 MB copy per layer.moe_stats()also reportsweight_uploads/weight_upload_s.test_hot_expert_packing: packing layout, and the batched graph equals the per-expert computation;test_moe_hot_experts_on_device_match_hostnow also checks the call count: 3 per layer (forward, the forward thatHostMLP.backwardrecomputes, backward), not 3 per expert.experiments/2026-10-21-moe-batched-hot-experts.md, with data and scripts inexperiments/data/moe-batched/.Verification
python -m pytest -q tests— 26 passed (torch 2.14.1+cpu, transformers 5.20.2). CI green.386b8bb, per-expert baselinemaina07aefc.main(per-expert calls)Reproduce:
python3 -m npu_lora.moe_bench2 MODEL DATA 0.25 out.jsonl 24, withexperiments/data/moe-batched/moe_bench2.pycopied intonpu_lora/. Precompile the buckets first withmoe_precompile.pyin a CPU Job (about 40 min per bucket).Compatibility and limits
moe-experts-E…instead ofmoe-expert-H…), so the old per-expert graphs are not reused. Each bucket compiles in about 40 min of CPU.HostMLP.backwardrecomputes.moe_replan_everywith hot experts; re-planning every step costs uploads.main's head, before Qwen2 on RNGD: pass npu-lora check (fp32 q/k + centred k), no negative loss (host target logit) #43, which rounded the target logit, so they are not compared.Related Issue
Related to #35 (the batched path is done; faster-than-host is still open)
🤖 Generated with Claude Code