-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Add .NET 10 Preview 2 release notes -- Libraries and Runtime #9770
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
Conversation
Inlining of Late Devirtualized Virtual MethodsIn .NET, the JIT compiler can optimize virtual method calls by replacing them with non-virtual calls when it can determine the exact type of the IC obj = GetObject();
obj.M();
IC GetObject() => new C();
interface IC
{
void M();
}
class C : IC
{
public void M() => Console.WriteLine(42);
} If the call to Devirtualize based on Inlining ObservationDuring inlining, a temporary variable may be created to hold the return value from the callee. With dotnet/runtime#111948, the JIT analyzes and updates the type of this temporary variable accordingly. If all return sites in a callee yield the same exact type, this precise type information is leveraged to devirtualize subsequent calls. With the above two improvements, along with recent efforts on array de-abstraction, the JIT can now devirtualize, inline, stack-allocate, and then perform struct promotion on arbitrary enumerators, so that the enumerator abstraction can be entirely eliminated, even without PGO. Consider the following example: var r = GetRangeEnumerable(0, 10);
foreach (var i in r)
{
Console.WriteLine(i);
}
static IEnumerable<int> GetRangeEnumerable(int start, int count) => new RangeEnumerable(start, count);
class RangeEnumerable(int start, int count) : IEnumerable<int>
{
public class RangeEnumerator(int start, int count) : IEnumerator<int>
{
private int _value = start - 1;
public int Current => _value;
object IEnumerator.Current => Current;
public void Dispose() { }
public bool MoveNext()
{
_value++;
return count-- != 0;
}
public void Reset() => _value = start - 1;
}
public IEnumerator<int> GetEnumerator() => new RangeEnumerator(start, count);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} With recent JIT improvements, the compiler now produces fully optimized code that devirtualizes and inlines all virtual calls, while also eliminating the enumerator allocation, which is achieved through escape analysis and struct promotion, allowing the enumerator to be entirely stack-allocated, resulting in zero heap allocations and optimal performance: ...
G_M27646_IG02:
mov ebx, 10
mov r15d, -1
jmp SHORT G_M27646_IG04
G_M27646_IG03:
mov edi, r15d
call [System.Console:WriteLine(int)]
mov ebx, r14d
G_M27646_IG04:
inc r15d
lea edi, [rbx-0x01]
mov r14d, edi
test ebx, ebx
jne SHORT G_M27646_IG03
... See the full comparison of codegen between .NET 9 and .NET 10 here. Support for Casting and Negation in NativeAOT Type PreinitializerNativeAOT includes a type preinitializer that can execute type initializers (static constructors) without side effects at compile time using an IL interpreter. The results are then embedded directly into the binary, allowing the elimination of these initializers entirely. With dotnet/runtime#112073, support has been extended to cover all variants of |
Looks great @hez2010 -- Can you create a PR on the branch? Can you define "late devitalization" in the text? "late" can be used in multiple ways, like "late-stage capitalism". We want to make it clear to readers. As much as possible, can you align with the format we've used in release notes previously, to match? Also, if that's too much, I'm happy to do this myself. |
If it's easier, I can add @hez2010's notes to my batch of JIT notes. |
Sounds good @amanasifkhalid. Let's do that. |
I have updated the original comment to reflect these.
Given it is going to be included in your JIT notes, I will not send a separate PR for this. |
|
||
Notice how `foreach_opaque_array_via_interface` allocates memory on the heap, suggesting the JIT failed to stack-allocate and promote the enumerator to registers. This is because the JIT relies on a technique called escape analysis to enable stack allocation. Escape analysis determines if an object's lifetime can exceed that of its creation context; if the JIT can guarantee an object will not outlive the current method, it can safely allocate it on the stack. In the above example, calling an interface method on the enumerator to control iteration causes it to escape, as the call takes a reference to the enumerator object. On the fast path of the type test, the JIT can try to devirtualize and inline these interface calls to keep the enumerator from escaping. However, escape analysis typically considers the whole method context, so the slow path's reliance on interface calls prevents the JIT from stack-allocating the enumerator at all. | ||
|
||
[dotnet/runtime #111473](https://github.com/dotnet/runtime/pull/111473) introduces conditional escape analysis -- a flow-sensitive form of the technique -- to the JIT. Conditional escape analysis can determine if an object will escape only on certain paths through the method, and prompt the JIT to create a fast path where the object never escapes. For array enumeration scenarios, conditional escape analysis reveals the enumerator will escape only when type tests for the collection fail, enabling the JIT to create a copy of the iteration code where the enumerator is stack-allocated and promoted. [benchmark results] |
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.
@AndyAyersMS do you have updated benchmark runs for the opaque case? Also I'm guessing the above benchmarks I took from your conditional escape analysis doc aren't up-to-date, now that we have @hez2010's changes in.
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.
These are tracked in the benchmark system, eg https://pvscmdupload.z22.web.core.windows.net/reports/allTestHistory/refs/heads/main_x64_ubuntu%2022.04/ViperUbuntu/ArrayDeAbstraction.foreach_opaque_array_via_interface.html
If you want a table I can run some stuff locally and get you something.
Also the array alloc revert has made it into P2.
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 don't believe the more recent changes from @hez2010 impacted the lab benchmark numbers since PGO could already get those cases (neither PR above has any lab annotations).
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.
Yeah changes to late devirt were mainly for non-PGO scenarios, PGO benchmark shouldn't be affected (the guard created by GDV was blocking stackalloc before conditional escape analysis).
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.
Here's some locally collected data.
Method | Toolchain | Mean | Median | Ratio | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|
foreach_static_readonly_array | net9.0 | 150.78 ns | 150.72 ns | 1.00 | - | NA |
foreach_static_readonly_array_via_interface_property | net9.0 | 851.75 ns | 849.93 ns | 5.65 | 32 B | NA |
foreach_opaque_array_via_interface | net9.0 | 874.66 ns | 877.68 ns | 5.80 | 32 B | NA |
foreach_static_readonly_array | net10.0p2 | 151.75 ns | 151.14 ns | 1.01 | - | NA |
foreach_static_readonly_array_via_interface_property | net10.0p2 | 280.04 ns | 278.77 ns | 1.86 | - | NA |
foreach_opaque_array_via_interface | net10.0p2 | 277.89 ns | 277.16 ns | 1.84 | - | NA |
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.
Thank you both, I'll update the tables
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.
For Non-PGO you can check the benchmark numbers here: dotnet/runtime#111948 (comment)
It was benchmarked without tiering compilation to demonstrate how late devirt inlining improvements interact with array de-abstraction.
|
||
This is about the feature | ||
Preview 1 introduced enhancements to the JIT compiler's devirtualization abilities for array interface methods, enabling the JIT to begin removing the abstraction overhead of array iteration via enumerators. Consider the following benchmarks: |
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 say Preview 2? Or is this a Preview 1 feature we didn't call out before, or something being improved on furth in preview 2? This is confusing 😕
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.
or something being improved on further in preview 2
It's this case. I'll add something here to transition into Preview 2-specific work.
IC obj = GetObject(); | ||
obj.M(); | ||
|
||
IC GetObject() => new C(); |
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.
If I understand correctly, it is usually devirt that enables inlining. In this case, it is the opposite, that we need inlining to enable devirt, which may allow further inlining. Fair? If so, this example is great to talk to.
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.
You're correct, the two tend to create virtuous cycles out of each other.
Thanks everyone. The contributions look great! |
* start preview 2 release notes * Add feature section to WPF release notes Fix linter issue * updates for linter * updates * add release notes for efcore * small fix to the fixed bugs link * typo * added mention of another improvement * .NET MAUI in 10 preview 2 Starting the release notes gathering here. @dalexsoto @rolfbjarne @jonpryor @PureWeen @Redth please contribute. * Add ExportPkcs12 blurb * Fix bad newlines from Copilot Workspace * add release note about SDK noun-first command forms * Update known issues to include ARM64 Windows hosting bundle upgrade break (#9783) * Update known issues to include ARM64 Windows hosting bundle upgrade break * Apply suggestions from code review Co-authored-by: Daniel Roth <[email protected]> * Add ASP.NET Core updates for .NET 10 Preview 2 (#9801) * Add ASP.NET Core updates for .NET 10 Preview 2 * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Martin Costello <[email protected]> * Add Blazor reconnection UI component * Add remaining Blazor features * Edits * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Safia Abdalla <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Safia Abdalla <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Safia Abdalla <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Luke Latham <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Luke Latham <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Luke Latham <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Luke Latham <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Luke Latham <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Luke Latham <[email protected]> * Add ShouldMatch code snippet * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Wade Pickett <[email protected]> * Update release-notes/10.0/preview/preview2/aspnetcore.md Co-authored-by: Wade Pickett <[email protected]> --------- Co-authored-by: Martin Costello <[email protected]> Co-authored-by: Safia Abdalla <[email protected]> Co-authored-by: Luke Latham <[email protected]> Co-authored-by: Wade Pickett <[email protected]> * Add containers release notes for 10.0 Preview 2 (#9796) * Add containers release notes for 10.0 Preview 2 * Fix run-on-sentence Co-authored-by: Matt Thalman <[email protected]> --------- Co-authored-by: Matt Thalman <[email protected]> * WinForms Preview 2 Release Notes (#9787) * Preview 2 * Fix typo * Update query * Review feedback * extra line * Fixing errors * Initial version C# P2 blog post (#9795) * Initial version C# P2 blog post * Apply suggestions from code review Co-authored-by: Bill Wagner <[email protected]> * updat ename of file * Update release-notes/10.0/preview/preview2/csharp.md --------- Co-authored-by: James Montemagno <[email protected]> Co-authored-by: Bill Wagner <[email protected]> * Preview 2 release notes for WPF (#9805) * Update wpf.md * Add missing period. * cleanup * cleanup * Add .NET 10 Preview 2 release notes -- Libraries and Runtime (#9770) * Start runtime and library release notes * Add JIT notes * Update benchmarks * Fix array de-abstraction intro * Update release-notes/10.0/preview/preview2/runtime.md --------- Co-authored-by: Aman Khalid <[email protected]> * MAUI in 10 preview 2 (#9782) * MAUI in 10 preview 1 * what's new for maui thanks davidbritch for capturing these * Update URLs for macios release notes * Final polishing --------- Co-authored-by: Alex Soto <[email protected]> Co-authored-by: Rachel Kang <[email protected]> * updates * updates to readme * update readme * tweaks to mds * enhacne tables * lint cleanup * Updating 10.0 PReview files * Update releases-index.json * updates --------- Co-authored-by: Rich Lander <[email protected]> Co-authored-by: maumar <[email protected]> Co-authored-by: David Ortinau <[email protected]> Co-authored-by: Jeremy Barton <[email protected]> Co-authored-by: Chet Husk <[email protected]> Co-authored-by: Stephen Halter <[email protected]> Co-authored-by: Daniel Roth <[email protected]> Co-authored-by: Martin Costello <[email protected]> Co-authored-by: Safia Abdalla <[email protected]> Co-authored-by: Luke Latham <[email protected]> Co-authored-by: Wade Pickett <[email protected]> Co-authored-by: Logan Bussell <[email protected]> Co-authored-by: Matt Thalman <[email protected]> Co-authored-by: Merrie McGaw <[email protected]> Co-authored-by: Kathleen Dollard <[email protected]> Co-authored-by: Bill Wagner <[email protected]> Co-authored-by: Andy (Steve) De George <[email protected]> Co-authored-by: Aman Khalid <[email protected]> Co-authored-by: Alex Soto <[email protected]> Co-authored-by: Rachel Kang <[email protected]> Co-authored-by: Rahul Bhandari (.NET) <[email protected]>
@AndyAyersMS thanks for catching that, I opened #9813 to fix this |
This is the space for libraries and runtime improvements. We'll aim to merge this PR by March 7th to align with the planned ship date of March 11th.
Everyone is welcome to add feature descriptions and participate in this process.
Parent PR/branch: #9769.