Skip to content

Optimize activation factory using binary search to make it 5x+ faster - #1633

Open
Yexuan Xiao (YexuanXiao) wants to merge 4 commits into
microsoft:masterfrom
YexuanXiao:opti-activation
Open

Yexuan Xiao (YexuanXiao) wants to merge 4 commits into
microsoft:masterfrom
YexuanXiao:opti-activation

Conversation

@YexuanXiao

@YexuanXiao Yexuan Xiao (YexuanXiao) commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Currently, C++/WinRT uses reverse-order string comparison and checks strings one by one with if statements to find the appropriate make function, which can be very inefficient. I observed that the WinUI repository uses 219 runtime classes (the module.g.cpp from my local WinUI build), and searching through them sequentially is undoubtedly slow and inefficient. By switching to binary search, the speed can be improved by a factor of five or more.
I wrote a benchmark that simulates both the original lookup logic and the updated logic. https://github.com/YexuanXiao/winrt-activation-bench
Before optimization, a single lookup took 170 ns; after optimization, it takes 30 ns.

@YexuanXiao Yexuan Xiao (YexuanXiao) changed the title Optimize activation factory using binary search Optimize activation factory using binary search to make it 5x+ faster Oct 4, 2026
@YexuanXiao

Copy link
Copy Markdown
Contributor Author

Jon Wiswall (@jonwis) Ryan Shepherd (@DefaultRyan) Can you take a look at this PR?

@jonwis

Copy link
Copy Markdown
Member

There are some DLLs in Windows that link multiple static libraries together into a single main.cpp (not even the module.g.cpp, hand-authored) entrypoint that rely on this "I know that winrt_make_typename exists, so I cut & paste it here and avoid a mess of #include and inclusion paths." I'm not sure this model will enable that.

Maybe try splitting apart the two steps? I agree that "search a table of names" is likely better than "scan." So your writer produces an constexpr array of std::pair<std::wstring_view, void* (*)()> for searching for this module, but dispatches the call through the function pointer. Converting callers to fix inclusion graphs between distant components may be more of a challenge.

@YexuanXiao

Yexuan Xiao (YexuanXiao) commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Jon Wiswall (@jonwis) My patch only affects module.g.cpp, so if the entry point is hand-written, there's nothing it can do. It generates two separate constexpr tables, one of string_views and one of function pointers. Once the type is found from the string_view table, the make function is called from the same position in the function pointer table. There's a macro for generating the declarations and these two tables, avoiding having to write the function name twice (once for the function declaration and once for the table element). Since the tables are only used once, I didn't use std::pair, which makes it possible to call lower_bound without writing a lambda, and allows it to be merged with other lower_bound<string_view*> calls.

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.

2 participants