Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Add MethodImplOptions.AggressiveOptimization and use it for tiering #20009

Merged
merged 8 commits into from
Oct 3, 2018

Conversation

kouvel
Copy link
Member

@kouvel kouvel commented Sep 17, 2018

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

@kouvel kouvel added this to the 3.0 milestone Sep 17, 2018
@kouvel kouvel self-assigned this Sep 17, 2018
@kouvel kouvel added the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Sep 17, 2018
@kouvel
Copy link
Member Author

kouvel commented Sep 17, 2018

m_optTier(NativeCodeVersion::OptimizationTier0),
#ifdef FEATURE_TIERED_COMPILATION
m_optTier(
pMethodDesc->RequestedAggressiveOptimization()
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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

Copy link
Member

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

@@ -334,7 +334,7 @@ PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig)
#endif

#ifdef FEATURE_READYTORUN
if (pCode == NULL)
if (pCode == NULL && !RequestedAggressiveOptimization())
Copy link
Member

@jkotas jkotas Sep 18, 2018

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
Copy link
Member

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.

Copy link
Member Author

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?

Copy link
Member

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.

Copy link
Member Author

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.

@@ -1752,9 +1752,15 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
BOOL fWasPromotedToTier1 = FALSE;
if (fEligibleForTieredCompilation)
{
fEligibleForCallCounting = g_pConfig->TieredCompilation_CallCounting();
if (fEligibleForCallCounting)
if (RequestedAggressiveOptimization())
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member

@noahfalk noahfalk left a 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.

m_optTier(NativeCodeVersion::OptimizationTier0),
#ifdef FEATURE_TIERED_COMPILATION
m_optTier(
pMethodDesc->RequestedAggressiveOptimization()
Copy link
Member

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

@@ -336,7 +341,10 @@ NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() con
}
else
{
return NativeCodeVersion::OptimizationTier0;
return
Copy link
Member

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
Copy link
Member

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.

@@ -1752,9 +1752,15 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
BOOL fWasPromotedToTier1 = FALSE;
if (fEligibleForTieredCompilation)
{
fEligibleForCallCounting = g_pConfig->TieredCompilation_CallCounting();
if (fEligibleForCallCounting)
if (RequestedAggressiveOptimization())
Copy link
Member

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.

@kouvel
Copy link
Member Author

kouvel commented Sep 27, 2018

@dotnet-bot test this please

@kouvel
Copy link
Member Author

kouvel commented Sep 27, 2018

@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0)

@kouvel
Copy link
Member Author

kouvel commented Sep 27, 2018

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.

@AndyAyersMS
Copy link
Member

If we inform the jit about AggressiveOptimization via jit flags, the jit can't see if potential inlinees have this attribute.

An alternative is to pass this information via the internal method attribs, via a new CORINFO_FLG_ enumerant, then the jit can look for this attribute on inlinees.

kouvel added 6 commits October 1, 2018 10:34
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
@kouvel
Copy link
Member Author

kouvel commented Oct 1, 2018

Thanks @AndyAyersMS, fixed in the latest commit (also rebased on latest)

Copy link
Member

@AndyAyersMS AndyAyersMS left a comment

Choose a reason for hiding this comment

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

LGTM.

Copy link
Member

@noahfalk noahfalk left a 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)
Copy link
Member

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 👍

DWORD dwImplFlags = 0;
return
IsIL() && // only makes sense for IL methods, and this implies !IsNoMetadata()
SUCCEEDED(GetMDImport()->GetMethodImplProps(GetMemberDef(), NULL, &dwImplFlags)) &&
Copy link
Member

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.
Copy link
Member

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.

Copy link
Member Author

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.

@kouvel kouvel merged commit b68296c into dotnet:master Oct 3, 2018
@kouvel kouvel deleted the NoTierMethod branch October 3, 2018 15:52
@kouvel kouvel removed the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Oct 3, 2018
@kouvel
Copy link
Member Author

kouvel commented Oct 4, 2018

It looks like the change to MethodImplOptions.cs in CoreLib/shared was not mirrored to corert, is there something I need to do to initiate the mirroring?

@jkotas
Copy link
Member

jkotas commented Oct 4, 2018

@Anipik The mirror seems to be down. Could you please take a look?

@Anipik
Copy link

Anipik commented Oct 4, 2018

@jkotas its done

@kouvel
Copy link
Member Author

kouvel commented Oct 4, 2018

Thanks!

kouvel added a commit to kouvel/coreclr that referenced this pull request Oct 4, 2018
…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
kouvel added a commit to kouvel/coreclr that referenced this pull request Oct 4, 2018
…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
MichalStrehovsky added a commit to MichalStrehovsky/corert that referenced this pull request Oct 5, 2018
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.
jkotas pushed a commit to dotnet/corert that referenced this pull request Oct 5, 2018
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.
kouvel added a commit to kouvel/coreclr that referenced this pull request Oct 22, 2018
…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
stephentoub pushed a commit to dotnet/corefx that referenced this pull request Oct 25, 2018
…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
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…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
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants