Skip to content

JIT: Synthesize profile data when invalid counts are discarded - #133206

Merged
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-fix-discarded-pgo-synthesis
Sep 4, 2026
Merged

JIT: Synthesize profile data when invalid counts are discarded#133206
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-fix-discarded-pgo-synthesis

Conversation

@tannergooding

Copy link
Copy Markdown
Member

Edge-count reconstruction can discard stale, malformed, all-zero, or unreconstructable PGO data after profile incorporation has already begun. Running RepairLikelihoods afterward preserves the default edge likelihoods as profile-derived, preventing later heuristics from adjusting cold throw paths.

Use ResetAndSynthesize when incorporation clears fgPgoHaveWeights; successfully incorporated profiles continue through repair.

Note

This pull request description was generated by GitHub Copilot.

CopilotAI lite review requested due to automatic review settings September 3, 2026 21:53
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Sep 3, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@tannergooding
tannergoodingforce-pushed the tannergooding-fix-discarded-pgo-synthesis branch from 12220d4 to 0158547CompareSeptember 3, 2026 21:57
@tannergoodingtannergooding changed the title JIT: Resynthesize discarded profile dataJIT: Synthesize profile data after discarding invalid countsSep 3, 2026
@tannergoodingtannergooding changed the title JIT: Synthesize profile data after discarding invalid countsJIT: Synthesize profile data when invalid counts are discardedSep 3, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

It changes JIT PGO/profile-synthesis behavior in a way that can broadly affect optimization decisions and should get a final human review for correctness and perf/regression risk.

Review tier: Lite
Findings: None

What changed in this PR

This PR adjusts how the JIT finalizes profile incorporation when sparse edge-count PGO reconstruction fails after incorporation has started. Instead of always “repairing” likelihoods, it now detects when incorporation has cleared fgPgoHaveWeights and performs a full reset + heuristic resynthesis of edge likelihoods/weights.

Changes:

  • After incorporating edge or block counts, conditionally run RepairLikelihoods only if profile weights remain valid.
  • If edge-count reconstruction discards the profile (clears fgPgoHaveWeights), run ResetAndSynthesize to reinitialize likelihoods and recompute weights using normal heuristics.
FileDescription
src/​coreclr/​jit/​fgprofile.cppChooses between repairing likelihoods vs resetting + synthesizing when profile incorporation discards weights.

CopilotAI review requested due to automatic review settings September 3, 2026 22:01
@tannergooding

Copy link
Copy Markdown
MemberAuthor

CC. @AndyAyersMS, @EgorBo

Ran across this when investigating a C# side change around the span slice logic. In particular, the following managed diff invalidates the static PGO data:

- if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_length)+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))

However, because of the current setup we were trying to repair them and this causes us to track the edges as having been seeded from PGO even though they weren't due to it being stale. So the repair has nothing to handle and it gives them initial likelihoods of 0.5 which then gave the below, meaning the throw path was incorrectly predicted and never gets treated as cold like it should.

BB01 first check 100
BB02 second check 50
BB03 shared throw path 75
BB04 success/return 100

This fixes it so that invalidated PGO no longer prevents normal heuristics from kicking in and allows things to work "as normal". It should ideally help mitigate issues from out of date PGO when such changes are made and is short lived since the next PGO update should "fix" things to no longer be invalidated

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The change is narrowly scoped, uses existing synthesis modes, and correctly avoids re-marking discarded count data as profile-derived by switching to reset+synthesis when fgPgoHaveWeights is cleared.

Review tier: Lite
Findings: None

@AndyAyersMS

Copy link
Copy Markdown
Member

Looks like there are substantial SPMI diffs -- though no details for x64 yet. Either this is kicking in a lot more than we think, or our static PGO data is really stale.

Can you dig into some of these diffs?

@tannergooding

Copy link
Copy Markdown
MemberAuthor

@AndyAyersMS looks like it's mostly because of inlining methods whose dynamic PGO edge counters were present but all zero.

5,674 / 6,275 Windows x64 changes came through inlinees. In a sample of 185 affected inlinee occurrences, 141 had all-zero dynamic PGO and the other 44 had stale static PGO.

Essentially, if dynamic PGO is active but no profile is available for a method, we already synthesize weights using the normal heuristics. Likewise, if profile counts are available and usable, we keep them and repair any inconsistencies.

These diffs are primarily from the middle scenario where profile data existed but reconstruction rejected all of it. The flow graph initially gives conditional edges 50/50 likelihoods, and we were running the repair path after discarding the counts. Repair then kept those placeholders and treated them as profile-derived. Since this PR changes that to instead use normal synthesis (the no profile data available path) then an otherwise normal if/else with no special considerations (i.e. not a loop edge, not a block that always throws, etc), we instead get the 52/48 fallback preference.

A breakdown of one of the more common samples done by GPT is below...


One of the more common dynamic PGO cases looks like this:

if(value==null){FailArgumentNullException(parameterName);}

One ASP.NET SuperPMI context compiles SortedInt32KeyNode<T>.SetOrAdd, which has valid profile data of its own and inlines Requires.NotNull<T> nine times through its recursive helpers. The dynamic profile for Requires.NotNull<T> has the expected two edge counters, so it still matches the method, but both counters are zero. There is no measured branch ratio to preserve.

Previously we kept the initial 50/50 split as profile-derived. That prevented the later no-return adjustment from recognizing FailArgumentNullException as the cold path. With this change the inlinee is synthesized normally; once the no-return helper is identified, the null/throw edge is set to 0 and the normal edge to 1. Each copy is then scaled to its call site and imported back into SetOrAdd.

SuperPMI is replaying what the original process returned to the JIT here, rather than collecting new PGO itself. Inlinees are instrumented by default, so this is not an inherent limitation of dynamic PGO with inlining; these particular counters simply were not hit during collection.

@AndyAyersMS

Copy link
Copy Markdown
Member

@AndyAyersMS looks like it's mostly because of inlining methods whose dynamic PGO edge counters were present but all zero.

Interesting, yes it looks like this was making us overly aggressive in some cases. Thanks for the fix.

@tannergooding
tannergooding merged commit de29e26 into dotnet:mainSep 4, 2026
136 of 139 checks passed
@tannergooding
tannergooding deleted the tannergooding-fix-discarded-pgo-synthesis branch September 4, 2026 20:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclrCLR 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

@tannergooding@AndyAyersMS