Conversation
…ng the finder (PaddlePaddle#79798) `TorchProxyMetaFinder._find_spec_for_specific_module` looks up the real spec with `importlib.util.find_spec` while the proxy is disabled, which is what is meant to keep the finder out of its own lookup. That only holds while the finder is registered once: `enable_torch_proxy` inserts it into `sys.meta_path` unconditionally, so nested guards leave duplicates behind, while `disable_torch_proxy` drops a single occurrence. With a duplicate left in place the lookup re-enters the finder for the very same name and restarts the whole resolution, and a name that has no spec at all never converges, so the lookups compound. Measured with a missing submodule of a scoped package: registrations 1 2 3 4 lookups 1 4 15 64 Every lookup toggles the proxy, and every toggle walks all of `sys.modules` three times to move `torch*` in and out of the shadow table, so the cost is O(lookups * len(sys.modules)) -- it also grows as the program imports more, because that is what makes `sys.modules` bigger. Importing one package inside a scoped guard with 7 registrations in place expanded 7 probes for a missing module into 13699 lookups and 90M module name comparisons: 37.9s, of which 0.5s was the actual import work. Suppress the finder for the name it is currently resolving, so the outer lookup finishes instead of starting over; each registered copy is now asked exactly once. The proxy is still disabled around the lookup and `exec_module` still runs under its own guard, so the specs, loaders and `__enable_torch_proxy__` marks are unchanged -- verified on that same package: identical submodule set and identical set of marked modules, 37.9s -> 1.2s. The same package imported eagerly goes from 4.9s to 3.0s. Adaptations for release/3.4: - the guard is `use_torch_proxy_guard` on this branch, not `use_compat_guard`; the test uses `paddle.compat.use_torch_proxy_guard` accordingly. - `test/compat/fake_modules/torch_proxy_local_enabled_package/` does not exist on this branch, so it is brought in with the test that needs it. devPR: PaddlePaddle#79798 (cherry picked from commit 97a01e6)
risemeup1111
approved these changes
Sep 23, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## release/3.4 #79812 +/- ##
===============================================
Coverage ? 100.00%
===============================================
Files ? 1
Lines ? 15
Branches ? 0
===============================================
Hits ? 15
Misses ? 0
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
PR Category
Performance Optimization
PR Types
Performance
Description
devPR:#79798 (merged 2026-09-22, merge commit
7c722e5509)Cherry-pick of the above onto
release/3.4.TorchProxyMetaFinder._find_spec_for_specific_modulelooks up the real spec withimportlib.util.find_specwhile the proxy is disabled, which is what is meant to keep the finder out of its own lookup. That only holds while the finder is registered once:enable_torch_proxyinserts it intosys.meta_pathunconditionally, so nested guards leave duplicates behind, whiledisable_torch_proxydrops a single occurrence. With a duplicate left in place the lookup re-enters the finder for the very same name and restarts the whole resolution, and a name that has no spec at all never converges, so the lookups compound.Measured with a probe for a missing submodule of a package in the enabled scope:
sys.meta_pathEvery lookup toggles the proxy, and every toggle walks all of
sys.modulesto movetorch*in and out of the shadow table, so the cost isO(lookups * len(sys.modules))-- it also grows as the program imports more. Importing one package inside a scoped guard with 7 registrations already in place expanded the probes for a missing module into 13699 lookups and ~90M module-name comparisons: 37.9s, of which 0.5s was the actual import work.Fix: suppress the finder for the name it is currently resolving, so the outer lookup finishes instead of starting over. Each registered copy is now asked exactly once. The proxy is still disabled around the lookup and
exec_modulestill runs under its own guard, so the resulting specs, loaders and__enable_torch_proxy__marks are unchanged.Adaptations for
release/3.4use_torch_proxy_guardon this branch (notuse_compat_guard), so the one conflicting line keeps this branch's name; the new test usespaddle.compat.use_torch_proxy_guardaccordingly.test/compat/fake_modules/torch_proxy_local_enabled_package/does not exist on this branch, so it is brought in together with the test that needs it (two small files).The fix itself is identical to the develop commit:
import threading,_resolving/_resolving_nameson the finder, the earlyreturn Noneinfind_spec, and add/discard aroundimportlib.util.find_spec.Verification
The
proxy.pyon this branch matches the released 3.x wheel (both useuse_torch_proxy_guard), so the same patch could be applied to an installed paddle and the new test run against it for real:Full
test/compat/test_torch_proxy.py, A/B on the same interpreter:The 3 remaining failures are identical with the patch on and off, i.e. unrelated to this change (they come from running this branch's test file against a released wheel on a machine that also has real PyTorch installed).
Equivalence, measured on a package that loads 51 sonicmoe submodules under a scoped guard: identical submodule set, identical set of modules marked with
__enable_torch_proxy__(220), samequackmodule object before and after,quack.gemm_interface.gemm_symmetricstill importable.End to end: 8-node x 8-GPU pretraining ran 216 steps with this patch applied to the venv's paddle, loss 12.2834 -> 6.3311, memory flat, no import-related errors.
import paddlefleet_opsin the same interpreter: 4.89s -> 3.01s (A/B by disabling only the reentrancy bookkeeping, so the 1.88s is attributable to it alone).是否引起精度变化
否
Only import-time module resolution changes. No kernel, no computation and no reduction order is touched.