Skip to content

Speed up plugin discovery - #2194

Draft
JeanChristopheMorinPerso wants to merge 1 commit into
AcademySoftwareFoundation:mainfrom
JeanChristopheMorinPerso:optimize-plugin-discovery
Draft

Speed up plugin discovery#2194
JeanChristopheMorinPerso wants to merge 1 commit into
AcademySoftwareFoundation:mainfrom
JeanChristopheMorinPerso:optimize-plugin-discovery

Conversation

@JeanChristopheMorinPerso

@JeanChristopheMorinPerso JeanChristopheMorinPerso commented Sep 6, 2026

Copy link
Copy Markdown
Member

Leaving as draft until I get the time to polish and fully review the tests and think about potential unexpected side effects.

Summary

Plugin discovery was spending a surprising amount of time repeatedly scanning Python packages, installed entry-point metadata, and shared plugin configuration.

This changes the discovery path in three places:

  • Uses an os.scandir() fast path when looking for top-level packages that may contain rezplugins.
  • Discovers installed entry points once and shares the result across plugin types. The cache is invalidated when sys.path or its directory mtimes change.
  • Loads rezconfig once per plugin directory instead of once per plugin module.

ZIP files and custom importers still fall back to pkgutil, so those discovery mechanisms continue to work as before.

Performance

The benchmark repeatedly resets and loads all seven plugin types for 100 discovery cycles. It excludes the unrelated import optimizations investigated earlier.

Measurement Before After Improvement
Overall plugin discovery/loading 1.146 s 548.2 ms 2.09× faster
Top-level rezplugins discovery 1.203 s 297 ms 4.05× faster
Entry-point discovery 850 ms 249 ms 3.41× faster
Shared plugin config loading 231 ms 55 ms 4.20× faster

The individual timings are cumulative cProfile results over the repeated workload run using hyperfine, so they are not intended to add up to the hyperfine wall-clock result.

Testing

  • Added coverage ensuring entry-point metadata is discovered once across plugin types.
  • Added coverage ensuring entry points are rediscovered after sys.path changes.
  • Added coverage ensuring shared plugin configuration is loaded once per plugin path.
  • Existing plugin-manager tests pass.
  • Verified the optimized scanner returns the same ordered module set as pkgutil.iter_modules() in the benchmark environment.

AI usage

This was mostly generated by an LLM with a lot of babysitting.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0771d-16ef-7787-b60b-eb71b1cc8a4b (sorry in advanced for cursing a bit in that thread... The models were gaslighting me in multiple threads all trying to inline dozens of imports to find performance gains).

Signed-off-by: Jean-Christophe Morin <jean_christophe_morin@hotmail.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a0771d-16ef-7787-b60b-eb71b1cc8a4b
Co-authored-by: Amp <amp@ampcode.com>
@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.80328% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.44%. Comparing base (5c598c5) to head (3f1271a).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
src/rez/plugin_managers.py 91.80% 4 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2194      +/-   ##
==========================================
+ Coverage   61.29%   61.44%   +0.14%     
==========================================
  Files         164      164              
  Lines       20568    20629      +61     
  Branches     3575     3591      +16     
==========================================
+ Hits        12607    12675      +68     
+ Misses       7089     7084       -5     
+ Partials      872      870       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.



@lru_cache(maxsize=1)
def _get_entry_points(_cache_key):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is such a weird pattern. I'll have to rewrite this into something that looks more sane.

T = TypeVar("T")


def _entry_points_cache_key():

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, weird pattern.

_get_entry_points.cache_clear()


def _iter_top_level_modules():

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to review the correctness of this function.

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