Skip to content

fix: unregister offloaded MoE layers on release (#173) - #182

Merged
gspark-etri merged 2 commits into
optimus-rmfrom
fix/173-moe-registry-release
Oct 23, 2026
Merged

gspark-etri merged 2 commits into
optimus-rmfrom
fix/173-moe-registry-release

Conversation

@gspark-etri

Copy link
Copy Markdown
Collaborator

Background

opt_prime.accel.moe.ops._LAYERS only grows. Each OffloadedLayer holds the tier handles (host expert tensors, accelerator slots), and OffloadReport.release() freed the device leases but never the layers, so reloading a model in a server kept the old experts in host and GPU memory (#173: apply -> release -> del -> gc three times gave 2 -> 4 -> 6 registered layers).

Changes

  • opt_prime/opt_prime/accel/moe/ops.py: unregister_layer(layer_id) pops a layer from the registry.
  • opt_prime/opt_prime/accel/moe/api.py: apply_moe_offload records the ids it registers in a new OffloadReport.layer_ids field (repr=False, compare=False, left out of as_dict() like leases); release() unregisters them before releasing the leases.
  • opt_prime/tests/test_moe_offload.py: test_release_unregisters_layers, which checks that the model's layer ids are registered after apply_moe_offload and gone after release().

Verification

CPU plus one A100 (CUDA_VISIBLE_DEVICES=1), Python 3.10, torch 2.10.0+cu128, transformers 5.1.0, base optimus-rm @253be20.

  • New test on the unfixed code: TORCH_DEVICE_BACKEND_AUTOLOAD=0 CUDA_VISIBLE_DEVICES=1 PYTHONPATH=$PWD python3 -m pytest -q tests/test_moe_offload.py -k release -> 1 failed (assert not any(lid in ops._LAYERS for lid in lids): the ids are still registered after release()).
  • With the fix, MoE tests: pytest -q tests/test_moe_offload.py tests/test_moe_review.py tests/test_moe_train.py -> 116 passed, 2 skipped.
  • Full suite, from opt_prime/: TORCH_DEVICE_BACKEND_AUTOLOAD=0 CUDA_VISIBLE_DEVICES=1 PYTHONPATH=$PWD python3 -m pytest -q tests: before (@253be20, without the new test): 400 passed, 23 skipped, 135 subtests passed; after: 401 passed, 23 skipped, 135 subtests passed.
  • The audit repro (three apply -> release -> del -> gc rounds on build_tiny("qwen3_moe") with two cpu tiers) now prints 0 registered layers after each round (was 2 -> 4 -> 6).

Compatibility and limits

  • OffloadReport gains one field with a default; as_dict() output is unchanged. Callers that never call release() behave as before.
  • After release(), the offloaded model can no longer run (its OffloadedExperts modules refer to removed ids); release() was already documented as "after the offloaded model is no longer used".
  • Not covered: layers registered before an exception inside apply_moe_offload are still left in the registry (the except path releases only the leases). Not part of this issue.

Related Issue

Closes #173

🤖 Generated with Claude Code

gspark-etri and others added 2 commits October 23, 2026 09:56
The custom-op registry (ops._LAYERS) only grew: every OffloadedLayer, with
its host expert tensors and accelerator slots, stayed reachable after
release(), so reloading a model in a server kept the old experts in memory.
apply_moe_offload now records the registered layer ids in the report, and
release() pops them before releasing the leases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gspark-etri
gspark-etri merged commit 0006f54 into optimus-rm Oct 23, 2026
1 check passed
@gspark-etri
gspark-etri deleted the fix/173-moe-registry-release branch October 23, 2026 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant