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

Inliner improvements in .NET 9.0 #93069

Open
3 of 8 tasks
EgorBo opened this issue Oct 5, 2023 · 1 comment
Open
3 of 8 tasks

Inliner improvements in .NET 9.0 #93069

EgorBo opened this issue Oct 5, 2023 · 1 comment
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI User Story A single user-facing feature. Can be grouped under an epic.
Milestone

Comments

@EgorBo
Copy link
Member

EgorBo commented Oct 5, 2023

Overview

Inliner is one of the most impactful optimizations in terms of output code performance, primarily because it unlocks many other optimizations. Recently, we've started to more actively utilize various generic abstractions, such as Generic Math/GVMs, to unify existing code and eliminate code duplication. For instance, this allowed us to consolidate Formatting and Parsing logic for both UTF16 and UTF8 strings. Also, we've finally enabled Dynamic PGO by default which makes inliner even more aggressive for hot blocks. Consequently, we've begun encountering the inliner’s limitations more frequently. For .NET 9.0, we are planning the following improvements:

Enable inlining for shared generics requiring a runtime lookup

The simplest example is:

bool Test<T>() => Callee<T>();

bool Callee<T>() => typeof(T) == typeof(int);

Currently, Test<_Canon> never inlines Callee<> due to a runtime lookup needed (in fact, it will be eliminated). Another example that it's not allowed to cast Span<T> to ReadOnlySpan<T> efficiently, etc. It should fix issues like:

JanK proposed an algorithm for how we can enable it without too much effort: #81432 (comment) and I've already done initial preparations by moving runtime lookup expansion to a late phase in #81635

Re-think Inliner's time budget

Once inliner runs out of its Time Budget it starts to even ignore [AggressiveInlining] attributes which breaks user's assumptions and produces slower code. The idea is to re-think how it's calculated/checked and, possibly, redesign inliner to start from hot blocks first. The beneficiaries are:

  1. Library folks sometimes have to re-shuffle code or use NoInlining to make inliner happy - we should improve that experience
  2. 1P/3P code should hit less often and gain performance improvements from that

Good examples of the problem are #90090 and #81565

Enable inlining for methods with EH (Exception Handling)

Currently, we unconditionally give up on EH inside the callee even if in fact we'll remove EH blocks as dead code/redundant. Async/await state machine have some good candidates to benefit from this. Also, some C# expression produce try-finally blocks which JIT can eliminate depending on code (lock, foreach). Example:

void Foo()
{
    Bar();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Bar()
{
    try
    {
        Console.WriteLine("Hello");
    }
    catch
    {
    }
}

Produces:

Inlines into Program:Foo():this:
  [FAILED: has exception handling] Program:Bar():this

Proposed work

  • Inliner budget problem
    • Collect data how often we hit it in libraries and 1P/3P apps
    • Consider hot-block first approach for inlining
    • Consider using ML to estimate time it will take to jit a method
  • (Q4'23) Enable inlining for shared generics requiring a runtime lookup
    • Basic implementation that just repeats dictionary lookup inside the callee
    • Limit it to cases where it's useful (e.g. only for cases where we eliminate the lookup after inlining)
  • [Best effort] Enable inlining for methods with EH

cc @dotnet/jit-contrib @AndyAyersMS @JulieLeeMSFT

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Oct 5, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Oct 5, 2023
@ghost
Copy link

ghost commented Oct 5, 2023

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Issue Details

Overview

Inliner is one of the most impactful optimization in terms of ouput code performance, primary because it unlocks many other optimizations. Recently, we've started to more actively utilize various generic abstractions, such as Generic Math/GVMs, to unify existing code and eliminate code duplications. For instance, this allowed us to consolidate Formatting and Parsing logic for both UTF16 and UTF8 strings. Also, we've finally enabled Dynamic PGO by default which makes inliner even more aggressive for hot blocks. Consequently, we've begun encountering the inliner’s limitations more frequently. For .NET 9.0, we are planning the following improvements:

Enable inlining for shared generics requiring a runtime lookup**

The simplest example is:

bool Test<T>() => Callee<T>();

bool Callee<T>() => typeof(T) == typeof(int);

Currently, Test<_Canon> never inlines Callee<> due to a runtime lookup needed (in fact, it will be eliminated). Another example that it's not allowed to case Span<T> to ReadOnlySpan<T> efficiently, etc.. It should fix issues like:

JanK proposed an algorithm on how we can enable it with not too much efforts: #81432 (comment) and I've already done initial preparations by moving runtime lookup expansion to a late phase in #81635

Re-think Inliner's time budget

Once inliner runs out of its Time Budget it starts to even ignore [AggressiveInlining] attributes which brakes user's assumptions and producing slower code. The idea is to re-think how it's calculated/checked and, possibly, redesign inliner to start from hot blocks first. The beneficiars are:

  1. Library folks sometimes have to re-shuffle code or use NoInlining to make inliner happy - we should improve that experience
  2. 1P/3P code should hit less often and gain performance improvements from that

A good examples of the problem are #90090 and #81565

Enable inlining for methods with EH(Exception Handling)

Currently, we unconditionally give up on EH inside the callee even if in fact we'll remove EH blocks as dead code/redundant. Async/await state machine have some good candidates to benefit from this. Also, some C# expression produce try-finally blocks which JIT can eliminate depending on code (lock, foreach). Example:

void Foo()
{
    Bar();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Bar()
{
    try
    {
        Console.WriteLine("Hello");
    }
    catch
    {
    }
}

Produces:

Inlines into Program:Foo():this:
  [FAILED: has exception handling] Program:Bar():this

Proposed work

  • Inliner budget problem
    • Collect data how often we hit it in libraries and 1P/3P apps
    • Consider hot-block first approach for inlining
    • Consider using ML to estimate time it will take to jit a method
  • Enable inlining for shared generics requiring a runtime lookup
    • Basic implementation that just repeates dictionary lookup inside the callee
    • Limit it to cases where it's useful (e.g. only for cases where we eliminate the lookup after inlining)
  • [Best effort] Enable inlining for methods with EH

cc @dotnet/jit-contrib @AndyAyersMS @JulieLeeMSFT

Author: EgorBo
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@EgorBo EgorBo removed the untriaged New issue has not been triaged by the area owner label Oct 5, 2023
@EgorBo EgorBo self-assigned this Oct 5, 2023
@EgorBo EgorBo added this to the 9.0.0 milestone Oct 5, 2023
@JulieLeeMSFT JulieLeeMSFT added the User Story A single user-facing feature. Can be grouped under an epic. label Oct 6, 2023
@JulieLeeMSFT JulieLeeMSFT added this to Team User Stories in .NET Core CodeGen Oct 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI User Story A single user-facing feature. Can be grouped under an epic.
Projects
Status: Team User Stories
.NET Core CodeGen
  
Team User Stories
Development

No branches or pull requests

2 participants