Skip to content

perf: optimize C# loader to match legacy load times (#3268)#3270

Merged
romangolev merged 11 commits intodevelopfrom
tay0thman-fix-perf-3268
Apr 7, 2026
Merged

perf: optimize C# loader to match legacy load times (#3268)#3270
romangolev merged 11 commits intodevelopfrom
tay0thman-fix-perf-3268

Conversation

@tay0thman
Copy link
Copy Markdown
Contributor

@tay0thman tay0thman commented Apr 7, 2026

  • Update XML doc <remarks> for PyRevitConfig.Load() to accurately reflect that when loading from the default path, the method now eagerly creates the config file (and its parent directory) if missing — and that custom-path calls are uncached and never create files
  • Fix thread-safety in PyRevitConfig: mark _defaultInstance as volatile (required for correct double-checked locking in C#) and hold _cacheLock in ClearCache() to prevent a race with the write inside Load()

- Cache PyRevitConfig.Load() as a static singleton with session-scoped invalidation
- Cache Roslyn MetadataReference list and assembly build fingerprint as statics
- Replace per-extension AppDomain.GetAssemblies() scans with pre-built HashSet lookup
- Materialize libraryExtensions.ToList() once before the command loop
- Move hook registration into C# SessionManagerService to eliminate redundant Python-side extension re-parse
- Remove icon pre-loading step (File.ReadAllBytes on every icon) that legacy loader never had
- Limit ReadPythonScriptConstants to first ~50 lines per script file
- Auto-create empty pyRevit_config.ini if missing so Python configparser can save settings
- Wire PyRevitConfig.ClearCache() into ExtensionParser.ClearAllCaches() for proper reload invalidation

Before: 14.2s (C#) vs 12.7s (Legacy) — 11% slower
After:  13.2s (C#) vs 13.3s (Legacy) — on par or faster
@devloai
Copy link
Copy Markdown
Contributor

devloai bot commented Apr 7, 2026

Unable to trigger custom agent "Code Reviewer". You have run out of credits 😔
Please upgrade your plan or buy additional credits from the subscription page.

@tay0thman
Copy link
Copy Markdown
Contributor Author

@Wurschdhaud feel free to use the DLLs in the PR for your testing.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR targets pyRevit’s C# Roslyn-based loader performance regressions (vs. the legacy Python loader) by removing redundant work in the session load pipeline and introducing several session-scoped caches.

Changes:

  • Removes the Python-side “second pass” extension re-parse by moving hook registration into the C# SessionManagerService.
  • Adds session-scoped caching for config loading, Roslyn metadata references, assembly fingerprinting, and loaded-assembly detection.
  • Removes icon file pre-reading and reduces per-command allocations in command type generation.

Reviewed changes

Copilot reviewed 11 out of 27 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pyrevitlib/pyrevit/loader/sessionmgr.py Removes Python-side hook registration loop for the C# session path (now handled in C#).
dev/pyRevitLoader/pyRevitExtensionParser/PyRevitConfig.cs Adds a cached default config instance with ClearCache() and ensures missing ini is created.
dev/pyRevitLoader/pyRevitExtensionParser/ExtensionParser.cs Routes config caching through PyRevitConfig.Load(), clears config cache on reload, and exposes SanitizeClassName() for hook ID generation.
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/UIManagerService.cs Removes icon pre-loading step to match legacy behavior and reduce I/O.
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/SessionManagerService.cs Adds a dedicated hook-registration pass in the C# loader pipeline.
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/IconsHandling/IIconManager.cs Removes PreloadExtensionIcons() from the icon manager interface.
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/IconsHandling/IconManager.cs Removes the icon pre-read implementation.
dev/pyRevitLoader/pyRevitAssemblyBuilder/SessionManager/IHookManager.cs Expands hook registration API to accept runtime assembly + search paths inputs.
dev/pyRevitLoader/pyRevitAssemblyBuilder/SessionManager/HookManager.cs Implements C# hook discovery + EventHooks.RegisterHook() reflection calls.
dev/pyRevitLoader/pyRevitAssemblyBuilder/AssemblyMaker/CommandTypeGenerator.cs Materializes library extension directories once per extension to avoid repeated ToList() allocations.
dev/pyRevitLoader/pyRevitAssemblyBuilder/AssemblyMaker/AssemblyBuilderService.cs Caches loaded pyRevit assembly names and Roslyn metadata references; caches build fingerprint.
bin/netfx/engines/IPY342/pyRevitExtensionParser.dll Updates the packaged parser binary for this engine folder.

@tay0thman
Copy link
Copy Markdown
Contributor Author

@jmcouffin another copilot run will be helpful. updated and pushed a new commit.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 27 changed files in this pull request and generated 3 comments.

- Introduced TryParseHookFileName method to encapsulate hook file name parsing logic.
- Updated CreateHookId and BuildHookSearchPaths methods to internal access modifier for better encapsulation.
- Added HookManagerTests.cs to the project for improved test coverage.
@jmcouffin
Copy link
Copy Markdown
Contributor

@romangolev Please, we need your expert look here

@jmcouffin jmcouffin removed their request for review April 7, 2026 20:33
@romangolev
Copy link
Copy Markdown
Member

@tay0thman @jmcouffin this would be an awesome addition to the loader. Great job with hooks and caching 💪
With this pace we might me be able to get rid of the legacy loader soon
CatCatdrivingGIF

@romangolev romangolev merged commit 48d2de8 into develop Apr 7, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

📦 New work-in-progress (wip) builds are available for 6.3.0.26097+2314-wip

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.

5 participants