Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Jan 10, 2024

This PR:

  1. Changes the likelihood threshold, it used to be (UINT32)JitConfig.JitGuardedDevirtualizationChainLikelihood() (makes no sense) which is 75 and I changed it to 40 because any cast with two likelily classes won't be handed otherwise (50/50). It's based on our threshold for GDV for calls which is 20-30, I just made it a bit more conservative since the benefit is smaller.

  2. Adds ability to save weights (likelihoods) inside a Qmark.

Example:

for (int i = 0; i < 200; i++)
{
    Test(new Program());
    Thread.Sleep(10);
}


static void Test(object o)
{
    if (o is Program)
        Console.WriteLine();
}
; Assembly listing for method Program:Test(System.Object) (Tier1)
G_M3485_IG01:
       sub      rsp, 40
						;; size=4 bbWeight=1 PerfScore 0.25
G_M3485_IG02:
       mov      rax, rcx
       test     rax, rax
       je       SHORT G_M3485_IG04
						;; size=8 bbWeight=1 PerfScore 1.50
G_M3485_IG03:
       mov      rdx, 0xD1FFAB1E      ; Program
       cmp      qword ptr [rax], rdx
       jne      SHORT G_M3485_IG07
						;; size=15 bbWeight=0.50 PerfScore 2.12
G_M3485_IG04:
       test     rax, rax
       je       SHORT G_M3485_IG05
       call     [System.Console:WriteLine()]
						;; size=11 bbWeight=1 PerfScore 4.25
G_M3485_IG05:
       nop      
						;; size=1 bbWeight=1 PerfScore 0.25
G_M3485_IG06:
       add      rsp, 40
       ret      
						;; size=5 bbWeight=1 PerfScore 1.25
G_M3485_IG07:
       mov      rdx, rcx
       mov      rcx, 0xD1FFAB1E      ; Program
       call     CORINFO_HELP_ISINSTANCEOFCLASS
       jmp      SHORT G_M3485_IG04
						;; size=20 bbWeight=0 PerfScore 0.00

; Total bytes of code 64

CORINFO_HELP_ISINSTANCEOFCLASS is now a cold block (because our profile is monomorphic)

@ghost ghost assigned EgorBo Jan 10, 2024
@ghost ghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jan 10, 2024
@ghost
Copy link

ghost commented Jan 10, 2024

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

Issue Details

This PR:

  1. Changes the likelihood threshold, it used to be (UINT32)JitConfig.JitGuardedDevirtualizationChainLikelihood() (makes no sense) which is 75 and I changed it to 40 because any cast with two likelily classes won't be handed otherwise (50/50). It's based on our threshold for GDV for calls which is 20-30, I just made it a bit more conservative since the benefit is smaller.

  2. Adds ability to save weights (likelihoods) inside a Qmark.

Example:

for (int i = 0; i < 200; i++)
{
    Test(new Program());
    Thread.Sleep(10);
}


static void Test(object o)
{
    if (o is Program)
        Console.WriteLine();
}
; Assembly listing for method Program:Test(System.Object) (Tier1)
G_M3485_IG01:
       sub      rsp, 40
						;; size=4 bbWeight=1 PerfScore 0.25
G_M3485_IG02:
       mov      rax, rcx
       test     rax, rax
       je       SHORT G_M3485_IG04
						;; size=8 bbWeight=1 PerfScore 1.50
G_M3485_IG03:
       mov      rdx, 0xD1FFAB1E      ; Program
       cmp      qword ptr [rax], rdx
       jne      SHORT G_M3485_IG07
						;; size=15 bbWeight=0.50 PerfScore 2.12
G_M3485_IG04:
       test     rax, rax
       je       SHORT G_M3485_IG05
       call     [System.Console:WriteLine()]
						;; size=11 bbWeight=1 PerfScore 4.25
G_M3485_IG05:
       nop      
						;; size=1 bbWeight=1 PerfScore 0.25
G_M3485_IG06:
       add      rsp, 40
       ret      
						;; size=5 bbWeight=1 PerfScore 1.25
G_M3485_IG07:
       mov      rdx, rcx
       mov      rcx, 0xD1FFAB1E      ; Program
       call     CORINFO_HELP_ISINSTANCEOFCLASS
       jmp      SHORT G_M3485_IG04
						;; size=20 bbWeight=0 PerfScore 0.00

; Total bytes of code 64
Author: EgorBo
Assignees: EgorBo
Labels:

area-CodeGen-coreclr

Milestone: -

@EgorBo
Copy link
Member Author

EgorBo commented Jan 10, 2024

@AndyAyersMS @dotnet/jit-contrib PTAL,

  1. Change likelihood treshold from 75 to 40 (it's also defined as a in-place constant for GDV)
  2. Save weights inside QMARK.

Small diffs due to more blocks marked as cold (due to profile) https://dev.azure.com/dnceng-public/public/_build/results?buildId=522520&view=ms.vss-build-web.run-extensions-tab (I suspect the real diffs could be bigger but that needs new collections)

@EgorBo EgorBo requested a review from AndyAyersMS January 10, 2024 23:00
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.

Was this based on any regression? Lack of improvement? Or just something you realized made more sense?

@AndyAyersMS
Copy link
Member

In our class profile probes we ignore cases with null objects. I wonder if for casting we shouldn't ignore them, then you would know how often the null case might come up...?

We could make this change generally but would need to think through the repercussions on consumers; some of them are likely not expecting to see a null result.

For example:

HCIMPL2(void, JIT_ClassProfile32, Object *obj, ICorJitInfo::HandleHistogram32* classProfile)
{
    FCALL_CONTRACT;
    FC_GC_POLL_NOT_NEEDED();

    OBJECTREF objRef = ObjectToOBJECTREF(obj);
    VALIDATEOBJECTREF(objRef);

    size_t sampleIndex;
    if (!CheckSample(&classProfile->Count, &sampleIndex) || objRef == NULL)
    {
        return;
    }

    MethodTable* pMT = objRef->GetMethodTable();

could be

HCIMPL2(void, JIT_ClassProfile32, Object *obj, ICorJitInfo::HandleHistogram32* classProfile)
{
    FCALL_CONTRACT;
    FC_GC_POLL_NOT_NEEDED();

    OBJECTREF objRef = ObjectToOBJECTREF(obj);
    VALIDATEOBJECTREF(objRef);

    size_t sampleIndex;
    if (!CheckSample(&classProfile->Count, &sampleIndex))
    {
        return;
    }

    MethodTable* pMT = (objRef == NULL) ? NULL : objRef->GetMethodTable();

@EgorBo
Copy link
Member Author

EgorBo commented Jan 11, 2024

In our class profile probes we ignore cases with null objects. I wonder if for casting we shouldn't ignore them, then you would know how often the null case might come up...?

The part that changes weights definitely improves code densitiy and my microbenchmark. The changed threshold is purely speculative at this point. Although, there are CastingPerf benchmarks which test polymorphic casts. It's just that I noticed that a cast that has 50% Class1 and 50% Class2 is never expanded because the threshold was 75%. I think this all might be tunned because some casts are very expensinve while others are not.

In our class profile probes we ignore cases with null objects. I wonder if for casting we shouldn't ignore them, then you would know how often the null case might come up...?

That's a very good point - I was considering it too, I was just not sure how to trace null properly. I assume the empy table is basically all nulls, right? So how do I tell the difference between a table that is just almost empty (because we only had 1 hit) vs a very hot table that is half null because values are nulls?

Should I pre-init it with some magic value? Or it's not a bid deal to handle cold tables like hot ones?

@EgorBo
Copy link
Member Author

EgorBo commented Jan 11, 2024

I'll experiment with nulls separately

@EgorBo EgorBo merged commit 82ac648 into dotnet:main Jan 11, 2024
@EgorBo EgorBo deleted the improve-pgo-cast branch January 11, 2024 15:00
tmds pushed a commit to tmds/runtime that referenced this pull request Jan 23, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Feb 18, 2024
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.

3 participants