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

Don't re-instrument methods which don't need that #84772

Closed
wants to merge 3 commits into from

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Apr 13, 2023

Closes #84517

static void Main()
{
    for (int i = 0; i < 100; i++)
    {
        Test1();
        var _ = Test2;
        Thread.Sleep(16);
    }
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int Test1()
{
    int sum = 0;
    for (int i = 0; i < 1000; i++)
        sum += i;
    return sum;
}

static int Test2 { get; set; }

Was:

JIT compiled Program:Test1():int [Instrumented Tier0, IL size=24, code size=130, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Tier1-OSR @0xe with Dynamic PGO, IL size=24, code size=34, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Instrumented Tier0, IL size=24, code size=130, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Tier1-OSR @0xe with Dynamic PGO, IL size=24, code size=34, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Tier1 with Dynamic PGO, IL size=24, code size=17, hash=0x5d807ce4]
JIT compiled Program:get_Test2():int [Instrumented Tier0, IL size=6, code size=12, hash=0xc7bf650e]
JIT compiled Program:get_Test2():int [Tier0, IL size=6, code size=12, hash=0xc7bf650e]
JIT compiled Program:get_Test2():int [Tier1, IL size=6, code size=7, hash=0xc7bf650e]

Now:

JIT compiled Program:Test1():int [Instrumented Tier0, IL size=24, code size=130, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Tier1-OSR @0xe with Dynamic PGO, IL size=24, code size=34, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Tier1 with Dynamic PGO, IL size=24, code size=17, hash=0x5d807ce4]
JIT compiled Program:get_Test2():int [Tier0, IL size=6, code size=12, hash=0xc7bf650e]
JIT compiled Program:get_Test2():int [Tier1, IL size=6, code size=7, hash=0xc7bf650e]

What happened:

  1. get_Test2() is not promoted to Tier0-Instrumented since Tier0 realized it's too simple for that so it's promoted to Tier1 (it's also not needed here but that is a different issue)
  2. Test1() promoted itself to Tier0-Instrumented due to loops so it also now tells vm it doesn't need to be promoted to Tier0-Instrumented again (and discard OSR versions)

@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 Apr 13, 2023
@ghost ghost assigned EgorBo Apr 13, 2023
@ghost
Copy link

ghost commented Apr 13, 2023

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

Issue Details

Closes #84517

static void Main()
{
    for (int i = 0; i < 100; i++)
    {
        Test1();
        var _ = Test2;
        Thread.Sleep(16);
    }
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int Test1()
{
    int sum = 0;
    for (int i = 0; i < 1000; i++)
        sum += i;
    return sum;
}

static int Test2 { get; set; }

Was:

JIT compiled Program:Test1():int [Instrumented Tier0, IL size=24, code size=130, hash=0x5d807ce4]
JIT compiled Program:get_Test2():int [Tier0, IL size=6, code size=12, hash=0xc7bf650e]
JIT compiled Program:Test1():int [Tier1-OSR @0xe with Dynamic PGO, IL size=24, code size=34, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Instrumented Tier0, IL size=24, code size=130, hash=0x5d807ce4]
JIT compiled Program:get_Test2():int [Instrumented Tier0, IL size=6, code size=12, hash=0xc7bf650e]
JIT compiled Program:Test1():int [Tier1-OSR @0xe with Dynamic PGO, IL size=24, code size=34, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Tier1 with Dynamic PGO, IL size=24, code size=17, hash=0x5d807ce4]
JIT compiled Program:get_Test2():int [Tier1, IL size=6, code size=7, hash=0xc7bf650e]

Now:

JIT compiled Program:Test1():int [Instrumented Tier0, IL size=24, code size=130, hash=0x5d807ce4]
JIT compiled Program:get_Test2():int [Tier0, IL size=6, code size=12, hash=0xc7bf650e]
JIT compiled Program:Test1():int [Tier1-OSR @0xe with Dynamic PGO, IL size=24, code size=34, hash=0x5d807ce4]
JIT compiled Program:Test1():int [Tier1 with Dynamic PGO, IL size=24, code size=17, hash=0x5d807ce4]
JIT compiled Program:get_Test2():int [Tier1, IL size=6, code size=7, hash=0xc7bf650e]
Author: EgorBo
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@EgorBo
Copy link
Member Author

EgorBo commented Apr 13, 2023

/azp run runtime-coreclr pgo, runtime-coreclr libraries-pgo

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@EgorBo
Copy link
Member Author

EgorBo commented Apr 13, 2023

PTAL @AndyAyersMS thoughts on the approach?
@jkotas can we borrow a flag in MethodDesc for that?

@AndyAyersMS
Copy link
Member

Seems like you perhaps should keep this "don't need to instrument" bit on the NativeCodeVersion instead of the MethodDesc?

@jkotas
Copy link
Member

jkotas commented Apr 13, 2023

I agree with Andy.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 13, 2023

Thanks! Looks like it has plenty of space

1>class NativeCodeVersion	size(16):
1>	+---
1> 0	| StorageKind m_storageKind
1>  	| <alignment member> (size=4)
1> 8	| m_pVersionNode
1> 8	| <unnamed-type-m_synthetic> m_synthetic
1>	+---

considering that m_storageKind needs only 2 bits

PS: changed it to:

1>class NativeCodeVersion	size(16):
1>	+---
1> 0	| StorageKind m_storageKind
1> 2	| NativeCodeVersionFlag m_flags
1>  	| <alignment member> (size=4)
1> 8	| m_pVersionNode
1> 8	| <unnamed-type-m_synthetic> m_synthetic
1>	+---

@EgorBo
Copy link
Member Author

EgorBo commented Apr 13, 2023

/azp run runtime-coreclr pgo, runtime-coreclr libraries-pgo

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@EgorBo
Copy link
Member Author

EgorBo commented Apr 13, 2023

Moved to NativeCodeVersion, failures are #84798

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.

Changes LGTM, but I wonder...

Can we see the same effect with simple R2R methods? That is, would we do R2R -> Tier1 + instr -> Tier1 even if Tier1+instr did not add any probes because the method was trivial?

I wonder if a simpler way of detecting these cases is for the jit interface to track if a schema was created during jitting (that is, if allocPgoInstrumentationBySchema ever got called during this jit attempt).

  • If at Tier0 with allow eager instrumentation flag, and the JIT created a schema, bypass future instrumentation requests (JIT decided to do eager instrumentation)
  • If at Tier1 BBINSTR and the JIT did not create a schema when the VM requested one, then the current code is Tier1 quality, and so will not benefit from more rejitting.

This would consolidate the logic on the VM side.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 14, 2023

Ah, interesting idea, I'll try

@EgorBo EgorBo force-pushed the fix-redundant-instrumentations branch from 957fe5a to 78efc23 Compare April 17, 2023 23:31
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.

This version LGTM

@AndyAyersMS
Copy link
Member

libraries-pgo was green last I looked, so maybe fire off that or some other pgo stress leg?

@EgorBo
Copy link
Member Author

EgorBo commented Apr 18, 2023

/azp run runtime-coreclr pgo, runtime-coreclr libraries-pgo, runtime-coreclr pgostress

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@EgorBo
Copy link
Member Author

EgorBo commented Apr 18, 2023

PGO pipelines look good, failures are #84752 and #81360

@EgorBo
Copy link
Member Author

EgorBo commented Apr 18, 2023

@jkotas do vm changes look good to you to merge?

@jkotas
Copy link
Member

jkotas commented Apr 18, 2023

@kouvel Could you please take a look?

@jkotas jkotas requested a review from kouvel April 18, 2023 18:11
}

// Leave a note that this method is properly instrumented
currentVersion.SetInstrumented();
Copy link
Member

@kouvel kouvel Apr 19, 2023

Choose a reason for hiding this comment

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

NativeCodeVersion is a simple wrapper around either a MethodDesc or a NativeCodeVersionNode, and is typically passed by value. The default native code version doesn't have a node and just wraps a MethodDesc. So, it's not a good candidate for persistent storage of this type of info. This change to the code version would just modify the local copy and would not be persisted anywhere.

AFAIK there isn't a clear place currently where such info could be stored. CallCountingInfo may be a candidate, for instance see CallCountingManager::DisableCallCounting() and CallCountingInfo::CreateWithCallCountingDisabled(), which are used to indicate that a code version should not be tiered. However, currently it's not set up to store extra info aside from call counting, for example CallCountingManager::SetCodeEntryPoint() does something different if the CallCountingInfo already exists as opposed to when it's being created. It may be possible to enable precreating a CallCountingInfo for a code version by introducing a new CallCountingInfo::Stage something like NotStarted to indicate that call counting has not started, and to modify SetCodeEntryPoint() to do something similar to before in that case. If the CallCountingInfo already exists, it could be used as persistent storage, as the CallCountingManager has a hash table mapping a code version to a CallCountingInfo.

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe it's better to rollback to occupying a spare bit in MethodDesc m_flag2 then? This change is a sort of an optimization, not correctness. The problem with CallCountingStubs that they're created assyncronlsy like you pointed out

Copy link
Member

Choose a reason for hiding this comment

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

The info appears to be specific to a code version rather than a method desc, so I'm not sure the method desc would be an appropriate place for it

private:

enum NativeCodeVersionFlag : UINT16
Copy link
Member

Choose a reason for hiding this comment

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

I suggest using an enum class to avoid polluting the parent namespace, though this may need to be moved based on my other comment.

@ghost ghost added needs-author-action An issue or pull request that requires more info or actions from the author. and removed needs-author-action An issue or pull request that requires more info or actions from the author. labels Apr 19, 2023
CodeVersionManager* pCodeVersionManager = m_pMethodBeingCompiled->GetCodeVersionManager();
CodeVersionManager::LockHolder codeVersioningLockHolder;
ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(m_pMethodBeingCompiled);
NativeCodeVersion currentVersion = ilVersion.GetActiveNativeCodeVersion(m_pMethodBeingCompiled);
Copy link
Member

Choose a reason for hiding this comment

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

The active code version may have changed meanwhile, so it may not represent the current code version being jitted. Could use something like this instead:

PrepareCodeConfig *config = GetThread()->GetCurrentPrepareCodeConfig();

Along with config->GetCodeVersion().

@EgorBo EgorBo marked this pull request as draft June 11, 2023 21:36
@ghost ghost closed this Jul 11, 2023
@ghost
Copy link

ghost commented Jul 11, 2023

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@ghost ghost locked as resolved and limited conversation to collaborators Aug 14, 2023
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PGO: redundant Tier0-instr promotions
4 participants