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

Inline properties during importation #96325

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Suchiman
Copy link
Contributor

When we see a call to something in the shape of a property, we check if it does the same thing as an auto property and then pretend that we saw the LDFLD or STFLD instead of the CALL in the caller. This bypasses most of the inliner complexity and managed to improve the startup time of AvaloniaILSpy in my tests from 1483ms to 1373ms (7.5% improvement)

@EgorBo as discussed in discord

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Dec 27, 2023
@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 Dec 27, 2023
@ghost
Copy link

ghost commented Dec 27, 2023

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

Issue Details

When we see a call to something in the shape of a property, we check if it does the same thing as an auto property and then pretend that we saw the LDFLD or STFLD instead of the CALL in the caller. This bypasses most of the inliner complexity and managed to improve the startup time of AvaloniaILSpy in my tests from 1483ms to 1373ms (7.5% improvement)

@EgorBo as discussed in discord

Author: Suchiman
Assignees: -
Labels:

area-CodeGen-coreclr, community-contribution

Milestone: -

@Suchiman Suchiman force-pushed the alwaysInlineProps branch 2 times, most recently from afbe3ab to 0023bf1 Compare December 27, 2023 03:17
@@ -8685,6 +8687,24 @@ void Compiler::impImportBlockCode(BasicBlock* block)
combine(combine(CORINFO_CALLINFO_ALLOWINSTPARAM, CORINFO_CALLINFO_SECURITYCHECKS),
(opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT : CORINFO_CALLINFO_NONE),
&callInfo);

if (callInfo.kind == CORINFO_CALL &&
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to be skipped for debug code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

impTryFindField calls canInline which does this check

// If the callee wants debuggable code, don't allow it to be inlined
{
// Combining the next two lines, and eliminating jitDebuggerFlags, leads to bad codegen in x86 Release builds using Visual C++ 19.00.24215.1.
CORJIT_FLAGS jitDebuggerFlags = GetDebuggerCompileFlags(pCallee->GetModule(), CORJIT_FLAGS());
if (jitDebuggerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE))
{
result = INLINE_NEVER;
szFailReason = "Inlinee is debuggable";

Copy link
Member

Choose a reason for hiding this comment

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

This check is for callee. Inlining needs to be disabled for debuggable caller too. It is typically done by checking opts.compDbgCode in the JIT.

@@ -32,6 +32,107 @@
#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
#endif

bool Compiler::impTryFindField(CORINFO_METHOD_HANDLE methHnd, CORINFO_RESOLVED_TOKEN* pResolvedToken, OPCODE* opcode)
{
Copy link
Member

Choose a reason for hiding this comment

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

This should issue beginInlining / reportInliningDecision callbacks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Like this?

Copy link
Member

Choose a reason for hiding this comment

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

This seems like we can end up issuing duplicate callbacks now (e.g. this one failing, then regular inlining succeeding). In general it seems unfortunate to end up with two separate inliners if we can avoid it, so some way of integrating this behavior into the existing inliner would be much preferred. For example, this new inlining should most likely affect the inline tree that gets reported by reportRichMappings too, and proper debug information should be created to refer to it.

@Suchiman
Copy link
Contributor Author

@MihuBot

@EgorBo
Copy link
Member

EgorBo commented Jan 31, 2024

Tested this PR on a large 1P service:

Before

IL Bytes Jitted (B)                                           29,854,910
Number of Methods Jitted                                         335,997

After

IL Bytes Jitted (B)                                           29,449,476
Number of Methods Jitted                                         300,224

@AndyAyersMS
Copy link
Member

@Suchiman @EgorBo where are we at with this PR?

The approach seems promising, but we need to make sure we get all of the other effects of inlining handled properly.

@Suchiman
Copy link
Contributor Author

Suchiman commented Feb 27, 2024

@AndyAyersMS the PR is fully functional and correct enough to pass CI and improves startup speed as described in the initial post.
@EgorBo did offer to run crank to verify the gains but he didn't get around to that yet.

My hope was to get more numbers on this version (which will have the lowest possible overhead) and then investigate jakobs suggestion to integrate it more with the existing inliner, although i don't really see how that would work, and then get new numbers. Then we could compare this version, which might be a little bit ugly, with the architectural nicer version, to decide if the ugly'ness is worth it or not.

Egor did try inlining property sized methods in tier 0 code before but in his experiment, it regressed startup time (which motivated this PR in the first place), so i'm not hopeful that integrating this into the existing inliner will yield the same benefit. Egors experiment was based on a size treshold though, instead of precisely analyzing properties by IL as i do, which might move the bar but i would need to try.

@EgorBo
Copy link
Member

EgorBo commented Mar 4, 2024

@EgorBo did offer to run crank to verify the gains but he didn't get around to that yet.

I've just checked - it has no visible impact on TE. Mainly because TE benchmarks are too simplistic, there are only like 5k methods to jit. At the same time, this PR has nice impact on the 1P service I posted in #96325 (comment)

Egor did try inlining property sized methods in tier 0 code before but in his experiment, it regressed startup time (which motivated this PR in the first place), so i'm not hopeful that integrating this into the existing inliner will yield the same benefit. Egors experiment was based on a size treshold though, instead of precisely analyzing properties by IL as i do, which might move the bar but i would need to try.

Yes, mine was too generic, I expect yours to be more efficient in that scenario. The main issue with startup is that inlining may trigger unnecessary type load events etc, but, presumably, your case should not do that. So I'd just call the existing inline routine if an inlinee is precisely a getter/setter (so that we can avoid duplicating the logic and solve the debug info concerns Jakob raised). I'll collect new numbers if you do that

@JulieLeeMSFT
Copy link
Member

avoid duplicating the logic and solve the debug info concerns Jakob raised).

@Suchiman, could you please take care of this?

@JulieLeeMSFT JulieLeeMSFT added the needs-author-action An issue or pull request that requires more info or actions from the author. label Apr 1, 2024
@JulieLeeMSFT JulieLeeMSFT added this to the 9.0.0 milestone Apr 1, 2024
@Suchiman Suchiman force-pushed the alwaysInlineProps branch from b46461b to c319a5b Compare April 27, 2024 17:37
@dotnet-policy-service dotnet-policy-service bot removed needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity labels Apr 27, 2024
@JulieLeeMSFT JulieLeeMSFT requested a review from EgorBo May 20, 2024 15:43
@JulieLeeMSFT
Copy link
Member

@EgorBo, PTAL at this community PR.

@JulieLeeMSFT
Copy link
Member

@Suchiman, we see that you pushed some commits. Is it ready for code review or are you still working on it?

@Suchiman
Copy link
Contributor Author

I'm still working on the feedback, but am currently on vacation

@JulieLeeMSFT JulieLeeMSFT added the needs-author-action An issue or pull request that requires more info or actions from the author. label Jun 24, 2024
When we see a call to something in the shape of a property, we check if it does the same thing as an auto property and then pretend that we saw the LD(S)FLD or ST(S)FLD instead of the CALL in the caller

- resolve tokens in the scope and with the generic context of the callee instead of the caller
- call getFieldInfo with the scope of the callee to make visibility checks happy
- make sure the callee is non virtual
- respect basic no inlining instructions
- getMethodInfo returns a success bool but might also throw
@Suchiman Suchiman force-pushed the alwaysInlineProps branch from c319a5b to 9afe6f6 Compare July 22, 2024 00:53
@dotnet-policy-service dotnet-policy-service bot removed needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity labels Jul 22, 2024
@Suchiman
Copy link
Contributor Author

I've started work on the alternative approach here Suchiman@ad2c505 but i still need to play whack-a-mole with all the asserts that popped up from not having "MINOPTS" flags set in tier 0 compilation (OSR is specifically unhappy about that).

Is that going in the right direction @EgorBo ?

So I'd just call the existing inline routine if an inlinee is precisely a getter/setter (so that we can avoid duplicating the logic and solve the debug info concerns Jakob raised).

@JulieLeeMSFT
Copy link
Member

@EgorBo, PTAL.

@EgorBo
Copy link
Member

EgorBo commented Sep 1, 2024

Taking a look

@EgorBo
Copy link
Member

EgorBo commented Oct 7, 2024

I've started work on the alternative approach here Suchiman@ad2c505 but i still need to play whack-a-mole with all the asserts that popped up from not having "MINOPTS" flags set in tier 0 compilation (OSR is specifically unhappy about that).

Is that going in the right direction @EgorBo ?

So I'd just call the existing inline routine if an inlinee is precisely a getter/setter (so that we can avoid duplicating the logic and solve the debug info concerns Jakob raised).

Sorry for the delayed response. We plan to eventually move inliner into an utility we can invoke from different phases, but we're not there yet. I agree with Jakob that the way it's written in this PR looks a bit fragile due to copy-paste from the general inliner making it more complicated to maintain and it should be somehow unified.
I've made my own attempt to enable Tier0 inlining in the past in here EgorBo@eab2ff7 the only thing that I enabled it for everything and introduced a new JIT-EE api to query IL size for the inlinee, presumably, you shouldn't need it here

@JulieLeeMSFT JulieLeeMSFT added the needs-author-action An issue or pull request that requires more info or actions from the author. label Oct 7, 2024
@Suchiman
Copy link
Contributor Author

Suchiman commented Nov 4, 2024

due to copy-paste from the general inliner making it more complicated to maintain and it should be somehow unified.

I've been looking at unifying impTryFindField with impCheckCanInline for the past two days but they have less in common than apparent at first sight and use vastly different data structures, making that very non trivial.

I've made my own attempt to enable Tier0 inlining in the past in here EgorBo@eab2ff7

I'll try to give that a shot, otherwise, i'm running thin on options and motivation

@dotnet-policy-service dotnet-policy-service bot removed needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity labels Nov 4, 2024
@EgorBo
Copy link
Member

EgorBo commented Dec 1, 2024

We've discussed this internally and decided that we need to extract inliner into some standalone utility so we can invoke it from reasonably any (early) phase so copy-pasting existing logic is not an option

@Suchiman
Copy link
Contributor Author

Suchiman commented Dec 1, 2024

@EgorBo

We've discussed this internally and decided that we need to extract inliner into some standalone utility so we can invoke it from reasonably any (early) phase so copy-pasting existing logic is not an option

My preliminary results from porting this PR on top of EgorBo@eab2ff7 so far indicated a 14% regression compared to the 7% improvement of this PR. I haven't measured where the time is spent but i suspect its just overhead of the regular inliner.

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 community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants