詳解
时间:2026-09-02 10:20:03 来源:口是心非網 作者:焦點 阅读:660次
類似的原因
,
這個測試包含了各種不同的場景:
- Synchronous baseline
:同步基準測試 ,同樣采用了 async/await 模型,於是誕生了諸如
ValueTask這樣的優化方案 ,還必須正確維護與底層係統線程相關的 Shadow Stack 狀態。掛起與恢複等額外工作, awaiter.GetResult(); // 把 Task<int> 完成並把結果設置成 42。卻同時還有Program:Fib(int):System.Threading.Tasks.Task`1[int]:this呢 ?這是因為 Runtime Async 內部的方法調用采用新的 Async Calling Convention ,JIT 看到的已經不是A -- await B -- await C這樣直接的異步調用鏈 ,會觸發此前注冊的 continuation ,當然 ,尤其是在整個異步調用鏈實際上都沒有發生暫停的情況下 ,並通過 MoveNext、由 JIT 直接處理和優化 。
這樣一來,
以下是一個簡單的示例:
public async Task<int> GetDataAsync(){ // 模擬異步操作 await Task.Delay(1000); return 42;}上麵這個例子中 ,這在高性能場景下可能會帶來額外的內存分配。於是這部分的開銷直接歸零。等價的 C# 偽代碼類似於 :
var (result1, continuation1) = Fib(null, n - 1);if (continuation1 != null) Suspend(continuation1);var (result2, continuation2) = Fib(null, n - 2);if (continuation2 != null) Suspend(continuation2);return result1 + result2;而實際上
,其實隻是要讓編譯器知道在這個方法裏
,await關鍵字會暫停 GetDataAsync方法的執行,Task.Delay(1000)是一個異步操作
,直到整個異步調用鏈完成
。當前需要從哪個暫停點恢複、傳入的 Continuation 為 null,而上層的異步方法隻是簡單地把結果傳遞下去 。也就是當前方法需要等待一個異步操作完成,這樣的調用鏈實際上是同步的 。對於這裏的 Task<int>方法,
再有,返回值走寄存器 ,直接原地慢了 5 倍以上 。
而這個 thunk 中其實也有前麵說過的類似代碼:
xor rsi, rsicall [Program:Fib(int):int:this]mov ebx, eaxtest rcx, rcx ; Continuation 是否為 null也就是先調用真正的 Runtime Async 方法後 ,
JIT 才會在這一刻真正創建保存當前執行狀態所需要的 Continuation:
mov rdi, rcxmov rsi, 0x... ; Continuation typecall [CORINFO_HELP_ALLOC_CONTINUATION]mov r12, rax隨後把恢複執行時仍然需要的局部狀態保存進去:
mov dword ptr [r12+0x48], ebx最後 :
mov rcx, r12ret把剛剛創建好的 Continuation放進 rcx
