Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constant propagation directly from MarkStep #1771

Merged
merged 30 commits into from
Jan 22, 2021

Conversation

vitek-karas
Copy link
Member

Call constant propagation and branch removal processing directly from MarkStep:

  • Renames RemoveUnreachableBlocksStep to UnreachableBlocksOptimizer - it's not a step anymore
  • Rename members to use the _name naming convention (which is also used by MarkStep)
  • Removes the "go over all methods in all assemblies and process them" logic
  • Some small changes to the ProcessMethod API to be more suitable for the MarkStep
  • The class is now instantiated by MarkStep and only lives as long as MarkStep does
  • MarkStep.ProcessMethod calls the optimizer's ProcessMethod before doing anything else on the method (this is because the optimizer can modify methods body, so mark step should only look at the modified version of the method)

Some interesting statistics (on console helloworld):

  • Before this change the optimizer processed 73K methods (every method at least once - total number of methods is 72K). After the change the optimizer only processes 12K methods (so a huge reduction). This also means that the large dictionary maintained by the optimizer on all processed methods is now 20% of before this change.
  • Max stack size increased to 62 - this is because of different order of processing methods. The 62 comes from ThrowHelpers which initializes lot of resource strings (each a separate call to a different method), so at one point all of these dependencies are on the stack. This is not a problem, just slightly higher memory usage. It would become problematic if the stack depth reached several 1000s which is VERY unlikely to happen.
  • Number of rewritten methods is down to 20% (from 746 to 117) - all of those not rewritten are actually not used by the app
  • As far as I can tell, it produces the exact same output.

Very rough perf measurement on Blazor template app:

  • Before all the constant prop changes (SDK from early December 2020): 6.1 seconds (on average)
  • After this change: 3.4 seconds (on average)

Part of #1733

src/linker/Linker.Steps/MarkStep.cs Outdated Show resolved Hide resolved
src/linker/Linker.Steps/MarkStep.cs Outdated Show resolved Hide resolved
void ProcessMethods (Collection<TypeDefinition> types)
{
foreach (var type in types) {
if (type.IsInterface)
Copy link
Member

Choose a reason for hiding this comment

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

Looks like we no longer check for interfaces - does it just mean that default interface methods can have branches removed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch - and basically yes. I fixed it (unintentionally 😉 ). So I now added at least a basic test that branch removal works within default interface methods.

Add a test for branch removal in default interface methods
@vitek-karas vitek-karas merged commit d8c44b8 into dotnet:master Jan 22, 2021
@vitek-karas vitek-karas deleted the ConstantPropFromMark branch January 22, 2021 12:37
jeromelaban added a commit to unoplatform/Uno.DotnetRuntime.WebAssembly that referenced this pull request Jan 26, 2021
Includes fixes to improve linking performance dotnet/linker#1771
agocke pushed a commit to dotnet/runtime that referenced this pull request Nov 16, 2022
Call constant propagation and branch removal processing directly from `MarkStep`:
* Renames `RemoveUnreachableBlocksStep` to `UnreachableBlocksOptimizer` - it's not a step anymore
* Rename members to use the `_name` naming convention (which is also used by `MarkStep`)
* Removes the "go over all methods in all assemblies and process them" logic
* Some small changes to the `ProcessMethod` API to be more suitable for the `MarkStep`
* The class is now instantiated by `MarkStep` and only lives as long as `MarkStep` does
* `MarkStep.ProcessMethod` calls the optimizer's `ProcessMethod` before doing anything else on the method (this is because the optimizer can modify methods body, so mark step should only look at the modified version of the method)

Some interesting statistics (on console helloworld):
* Before this change the optimizer processed 73K methods (every method at least once - total number of methods is 72K). After the change the optimizer only processes 12K methods (so a huge reduction). This also means that the large dictionary maintained by the optimizer on all processed methods is now 20% of before this change.
* Max stack size increased to 62 - this is because of different order of processing methods. The 62 comes from `ThrowHelpers` which initializes lot of resource strings (each a separate call to a different method), so at one point all of these dependencies are on the stack. This is not a problem, just slightly higher memory usage. It would become problematic if the stack depth reached several 1000s which is VERY unlikely to happen.
* Number of rewritten methods is down to 20% (from 746 to 117) - all of those not rewritten are actually not used by the app
* As far as I can tell, it produces the exact same output.

Very rough perf measurement on Blazor template app:
* Before all the constant prop changes (SDK from early December 2020): 6.1 seconds (on average)
* After this change: 3.4 seconds (on average)

Commit migrated from dotnet/linker@d8c44b8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants