JIT: Optimize AsyncHelpers await helpers and enable inlining for them #128528
Draft
jakobbotsch wants to merge 14 commits into
Draft
JIT: Optimize AsyncHelpers await helpers and enable inlining for them #128528jakobbotsch wants to merge 14 commits into
AsyncHelpers await helpers and enable inlining for them #128528jakobbotsch wants to merge 14 commits into
Conversation
More specifically this allows inlining of any async call with only tail awaits. In those cases inlining can be done just by inheriting context handling of the inlining call (and by removing the tail await).
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors the runtime-async await helper pipeline to enable more inlining (when all awaits are “tail awaits”) and to reduce allocations by using cached, hand-rolled continuations for Task / ValueTaskSource paths. It also updates async thunk IL generation (CoreCLR + tool) and adjusts the JIT importer to support inlining for tail-await-only async callees and to inherit async context args from the inliner when needed.
Changes:
- Rewrites
AsyncHelpers.Await*helpers into a tail-await shape and introduces cachedTaskContinuation/ValueTaskSourceContinuationcontinuations to reduce continuation allocations. - Updates CoreCLR async thunk emission to tail-await
TransparentAwait*helpers (including new genericTransparentAwaitOfT) and mirrors the change in the managed type-system stub generator. - Updates the JIT importer to (a) allow inlining async callees when awaits are marked tail-await, and (b) propagate async context args from the inliner into inlined async calls.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tests/async/valuetask-source/valuetask-source.cs | Adds debug output (currently noisy) while validating scheduling-context flags in a ValueTaskSource test. |
| src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs | Makes ConfiguredTaskAwaiter<TResult>.m_task internal to enable fast-path access from AsyncHelpers. |
| src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs | Makes configured ValueTask awaitable backing fields internal for fast-path access. |
| src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs | Rewrites await helpers to tail-await and route through optimized AwaitTask / AwaitValueTaskSource paths. |
| src/coreclr/vm/corelib.h | Adds binder entry for AsyncHelpers.TransparentAwaitOfT. |
| src/coreclr/vm/asyncthunks.cpp | Emits thunk IL that tail-awaits TransparentAwait* and returns immediately (incl. generic Task<T> case). |
| src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | Mirrors thunk IL updates in the managed stub generator (adds ret after transparent await). |
| src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj | Switches async helper continuation sources to new TaskContinuation and ValueTaskSourceContinuation files; minor formatting tweaks. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncProfiler.CoreCLR.cs | Updates comment text to reflect new continuation type naming. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.ValueTaskSourceContinuation.cs | Renames/specializes continuation for IValueTaskSource* only and simplifies result handling accordingly. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.TaskContinuation.cs | Introduces cached continuation that can also perform context-queueing before dispatching the runtime-async task. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | Adds AwaitTask* helpers, splits ValueTask handling between Task vs ValueTaskSource, and updates dispatch logic to use new continuations/caches. |
| src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj | Includes the new continuation source files for NativeAOT CoreLib build. |
| src/coreclr/jit/importercalls.cpp | Enables inlining for tail-await-only async callees; adds async-context inheritance for inlined async calls; renames ldvirtftn helper. |
| src/coreclr/jit/compiler.h | Updates declarations for renamed async-arg insertion helper and adds async-context inheritance helper. |
| src/coreclr/debug/di/rsstackwalk.cpp | Updates comment text to reflect new continuation type naming. |
| // - in optimized (as in JitOptimizeAwait=1, which is the default) case | ||
| // we do not distinguish "false" config vs. having default/no scheduling context, | ||
| // and "None" is passed in either case. | ||
| System.Console.WriteLine(sUseContext.trace); |
Comment on lines
+7056
to
+7066
| //------------------------------------------------------------------------ | ||
| // impAddAsyncArgsToInlinedCall: | ||
| // Inherit async args from inlining call as part of a new async call. | ||
| // | ||
| // Arguments: | ||
| // call - The async call | ||
| // | ||
| // Remarks: | ||
| // Currently we only allow inlining of async calls when all awaits are tail | ||
| // awaits. In that case inlining is simplified as we can just inherit | ||
| // everything from the inlining call. |
Comment on lines
7100
to
+7112
| @@ -7050,7 +7109,7 @@ void Compiler::impSetupAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned pref | |||
| // Should be called before the 'this' arg is inserted, but after other IL args | |||
| // have been inserted. | |||
| // | |||
| void Compiler::impInsertAsyncContinuationForLdvirtftnCall(GenTreeCall* call) | |||
| void Compiler::impInsertAsyncArgsForLdvirtftnCall(GenTreeCall* call) | |||
| Debug.Assert(continuationContext is TaskScheduler { }); | ||
| TaskScheduler sched = (TaskScheduler)continuationContext; | ||
|
|
||
| // TODO: We do not need TaskSchedulerAwaitTaskContinuation here, just need to refactor its Run method... |
Comment on lines
+95
to
+96
| var taskSchedCont = new TaskSchedulerAwaitTaskContinuation(sched, (Action)RuntimeAsyncTask.m_action!, flowExecutionContext: false); | ||
| taskSchedCont.Run(Task.CompletedTask, canInlineContinuationTask: true); |
Comment on lines
+65
to
73
| [MethodImpl(MethodImplOptions.Async | MethodImplOptions.AggressiveInlining)] | ||
| [StackTraceHidden] | ||
| public static T Await<T>(Task<T> task) | ||
| { | ||
| TaskAwaiter<T> awaiter = task.GetAwaiter(); | ||
| if (!awaiter.IsCompleted) | ||
| if (!task.IsCompleted) | ||
| { | ||
| UnsafeAwaitAwaiter(awaiter); | ||
| TailAwait(); | ||
| return AwaitTask(task, ConfigureAwaitOptions.ContinueOnCapturedContext); | ||
| } |
Contributor
|
Tagging subscribers to this area: @dotnet/area-system-threading-tasks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AsyncHelpers.TailAwait)Based on #128320
ValueTaskPerfbenchmarks from dotnet/performance: