-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add MethodImplOptions.AggressiveOptimization and use it for tiering #20009
Conversation
d310b5f
to
edc2e6a
Compare
src/vm/codeversion.cpp
Outdated
m_optTier(NativeCodeVersion::OptimizationTier0), | ||
#ifdef FEATURE_TIERED_COMPILATION | ||
m_optTier( | ||
pMethodDesc->RequestedAggressiveOptimization() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this rather be initialized based on what was actually compiled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tier is only applicable to methods eligible for tiering, that currently includes methods with r2r-pregenerated code and excludes methods with fragile ngen-pregenerated code. For the eligible methods, the only reason currently they would use tier 1 initially is if they are flagged with AggressiveOptimization
. Whether the method has pregenerated code available is checked later before compiling the method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant whether this should be set based on what was JITed or going to be JITed.
If I am reading the code correctly, we will create a new NativeCodeVersionNode when we promote thing from Tier0 to Tier1; or when we actually JIT new code. We know the tier in both places.
The change is calling RequestedAggressiveOptimization
on more places than what I would expect it to be called. So I am trying to understand why all the calls are really required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh ok, yes that should be avoidable, I'll take a look
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also suggest hoisting this call out of the constructor, but only because I view the CodeVersionNode as being a simple data container that shouldn't embed policy. I threw together a quick suggestion here:
noahfalk@697bc9e
src/vm/prestub.cpp
Outdated
@@ -334,7 +334,7 @@ PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig) | |||
#endif | |||
|
|||
#ifdef FEATURE_READYTORUN | |||
if (pCode == NULL) | |||
if (pCode == NULL && !RequestedAggressiveOptimization()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check should not be required. I think we should assume that if there is code in R2R image, it was aggressively optimized if necessary.
// Skip methods marked with MethodImplOptions.AggressiveOptimization, they will be jitted instead | ||
return; | ||
} | ||
|
||
// READYTORUN: FUTURE: Producedure spliting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do always do this (regardless of R2R or fragile ngen?) We still use fragile for corelib in 2.2 and (for now) in 3.0.
Perhaps academic for now as there aren't any AggressiveOptimization
attributes in corelib, but somebody might add one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that fragile ngen code would be (at the moment) as good as tier 1 JIT. Is that not the case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume fragile NGEN still wouldn't use more recent hardware instructions, so that is one place it might differ from JIT tier1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change it for now, though thinking about it more, it may need to be revisited in the future for both r2r and fragile ngen. For methods that don't need any special new instructions and can be aggressively optimized, it may be worth pregenerating the code, and that would have to be determined by the JIT instead of this check.
src/vm/prestub.cpp
Outdated
@@ -1752,9 +1752,15 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT) | |||
BOOL fWasPromotedToTier1 = FALSE; | |||
if (fEligibleForTieredCompilation) | |||
{ | |||
fEligibleForCallCounting = g_pConfig->TieredCompilation_CallCounting(); | |||
if (fEligibleForCallCounting) | |||
if (RequestedAggressiveOptimization()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much slower can the call counting get with this call on the call counting path?
Note that RequestedAggressiveOptimization
will access metadata that may include taking locks when the metadata are in R/W mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I measured the call counting overhead in MusicStore, AllReady, and CscSource benchmarks, I don't see any noticeable difference with the changes. At the moment I think it's relatively easy to reduce the number of calls to RequestedAggressiveOptimization()
per prestub invocation to a 2 - one where you highlighted above, and another in TieredCompilationManager::GetJitFlags
. It would be possible to reduce that to 1 by plumbing the value through. Another simpler option may be to do what is done for NoInlining
, which uses a bit in MethodDescClassification
(mdcNotInline
) to store that info in the MethodDesc once. It looks like some bits are available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another simpler option may be to do what is done for NoInlining, which uses a bit in MethodDescClassification (mdcNotInline)
I do not think it is worth burning a MethodDesc bit for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oo shoot, I just realized I wrote the comments earlier today but never hit 'submit review'. Sorry for the late response Kount.
src/vm/codeversion.cpp
Outdated
m_optTier(NativeCodeVersion::OptimizationTier0), | ||
#ifdef FEATURE_TIERED_COMPILATION | ||
m_optTier( | ||
pMethodDesc->RequestedAggressiveOptimization() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also suggest hoisting this call out of the constructor, but only because I view the CodeVersionNode as being a simple data container that shouldn't embed policy. I threw together a quick suggestion here:
noahfalk@697bc9e
src/vm/codeversion.cpp
Outdated
@@ -336,7 +341,10 @@ NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() con | |||
} | |||
else | |||
{ | |||
return NativeCodeVersion::OptimizationTier0; | |||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should extract the policy into a distinct method, I suggest TieredCompilation::GetDefaultOptimizationTier
// Skip methods marked with MethodImplOptions.AggressiveOptimization, they will be jitted instead | ||
return; | ||
} | ||
|
||
// READYTORUN: FUTURE: Producedure spliting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume fragile NGEN still wouldn't use more recent hardware instructions, so that is one place it might differ from JIT tier1.
src/vm/prestub.cpp
Outdated
@@ -1752,9 +1752,15 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT) | |||
BOOL fWasPromotedToTier1 = FALSE; | |||
if (fEligibleForTieredCompilation) | |||
{ | |||
fEligibleForCallCounting = g_pConfig->TieredCompilation_CallCounting(); | |||
if (fEligibleForCallCounting) | |||
if (RequestedAggressiveOptimization()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest trying to keep the policy about which methods need call counting distinct from the code that causes the call counting to occur.
@dotnet-bot test this please |
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0) |
The failed "Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0)" appears to be a duplicate broken job, there is another job of the same configuration that was rerun and passed. @noahfalk I think this PR is ready to go, it would be great if you could take a look at the last commit when you're back. |
If we inform the jit about An alternative is to pass this information via the internal method attribs, via a new |
Part of fix for https://github.com/dotnet/corefx/issues/32235 Workaround for https://github.com/dotnet/coreclr/issues/19751 - Added and set CORJIT_FLAG_AGGRESSIVE_OPT to indicate that a method is flagged with AggressiveOptimization - For a method flagged with AggressiveOptimization, tiering uses a foreground tier 1 JIT on first call to the method, skipping the tier 0 JIT and call counting - When tiering is disabled, a method flagged with AggressiveOptimization does not use r2r-pregenerated code - R2r crossgen does not generate code for a method flagged with AggressiveOptimization
Thanks @AndyAyersMS, fixed in the latest commit (also rebased on latest) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo testing I mentioned in the comment
} | ||
} | ||
|
||
#ifndef DACCESS_COMPILE | ||
void NativeCodeVersion::SetOptimizationTier(NativeCodeVersion::OptimizationTier tier) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Being able to make this immutable is nice 👍
src/vm/method.hpp
Outdated
DWORD dwImplFlags = 0; | ||
return | ||
IsIL() && // only makes sense for IL methods, and this implies !IsNoMetadata() | ||
SUCCEEDED(GetMDImport()->GetMethodImplProps(GetMemberDef(), NULL, &dwImplFlags)) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be IsMiAggressiveOptimization(GetImplAttrs()), but I wouldn't worry about it unless you've got other changes to make in addition.
@@ -0,0 +1,152 @@ | |||
// Licensed to the .NET Foundation under one or more agreements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whether in this PR or a later one, it would be good to add a test that confirms a method marked with the AggressiveOptimization attribute doesn't JIT twice. Now that EventPipe appears to work uniformly across platforms and RuntimeEventSource is implemented I think you could build a test that solely uses an in-proc EventListener to observe the jit events. I should have a little sample laying around if its helpful though I haven't explored jit events specifically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll do this in a separate PR, I'll need to add a perf test as well. Filed https://github.com/dotnet/coreclr/issues/20229 to track both.
It looks like the change to |
@Anipik The mirror seems to be down. Could you please take a look? |
@jkotas its done |
Thanks! |
…otnet#20009) Add MethodImplOptions.AggressiveOptimization and use it for tiering Part of fix for https://github.com/dotnet/corefx/issues/32235 Workaround for https://github.com/dotnet/coreclr/issues/19751 - Added and set CORJIT_FLAG_AGGRESSIVE_OPT to indicate that a method is flagged with AggressiveOptimization - For a method flagged with AggressiveOptimization, tiering uses a foreground tier 1 JIT on first call to the method, skipping the tier 0 JIT and call counting - When tiering is disabled, a method flagged with AggressiveOptimization does not use r2r-pregenerated code - R2r crossgen does not generate code for a method flagged with AggressiveOptimization
…otnet#20009) Add MethodImplOptions.AggressiveOptimization and use it for tiering Part of fix for https://github.com/dotnet/corefx/issues/32235 Workaround for https://github.com/dotnet/coreclr/issues/19751 - Added and set CORJIT_FLAG_AGGRESSIVE_OPT to indicate that a method is flagged with AggressiveOptimization - For a method flagged with AggressiveOptimization, tiering uses a foreground tier 1 JIT on first call to the method, skipping the tier 0 JIT and call counting - When tiering is disabled, a method flagged with AggressiveOptimization does not use r2r-pregenerated code - R2r crossgen does not generate code for a method flagged with AggressiveOptimization
Picks up the new tiering-related `MethodImpl` option added in dotnet/coreclr#20009. I went ahead and also implemented the `NoOptimization` `MethodImpl` option because it's related and we need to have it to ship anyway.
Picks up the new tiering-related `MethodImpl` option added in dotnet/coreclr#20009. I went ahead and also implemented the `NoOptimization` `MethodImpl` option because it's related and we need to have it to ship anyway.
…otnet#20009) Add MethodImplOptions.AggressiveOptimization and use it for tiering Part of fix for https://github.com/dotnet/corefx/issues/32235 Workaround for https://github.com/dotnet/coreclr/issues/19751 - Added and set CORJIT_FLAG_AGGRESSIVE_OPT to indicate that a method is flagged with AggressiveOptimization - For a method flagged with AggressiveOptimization, tiering uses a foreground tier 1 JIT on first call to the method, skipping the tier 0 JIT and call counting - When tiering is disabled, a method flagged with AggressiveOptimization does not use r2r-pregenerated code - R2r crossgen does not generate code for a method flagged with AggressiveOptimization
…tes.AggressiveOptimization (#32322) * Expose MethodImplOptions.AggressiveOptimization Part of fix for https://github.com/dotnet/corefx/issues/32235 Depends on dotnet/coreclr#20009 API review: https://github.com/dotnet/corefx/issues/32235 * Include new test * Expose MethodImplAttributes.AggressiveOptimization and fix test API review: https://github.com/dotnet/corefx/issues/32628
…tes.AggressiveOptimization (dotnet/corefx#32322) * Expose MethodImplOptions.AggressiveOptimization Part of fix for https://github.com/dotnet/corefx/issues/32235 Depends on dotnet/coreclr#20009 API review: https://github.com/dotnet/corefx/issues/32235 * Include new test * Expose MethodImplAttributes.AggressiveOptimization and fix test API review: https://github.com/dotnet/corefx/issues/32628 Commit migrated from dotnet/corefx@3f0a1ee
Part of fix for https://github.com/dotnet/corefx/issues/32235
Workaround for https://github.com/dotnet/coreclr/issues/19751
CORJIT_FLAG_AGGRESSIVE_OPT
to indicate that a method is flagged withAggressiveOptimization
AggressiveOptimization
, tiering uses a foreground tier 1 JIT on first call to the method, skipping the tier 0 JIT and call countingAggressiveOptimization
does not use r2r-pregenerated codeAggressiveOptimization