詳解
Runtime Async 的內存分配都比傳統 async 少了很多 。.NET 的 Green Thread 實驗中發現 Green Thread 上做係統調用 1 億次,於是誕生了諸如 ValueTask這樣的優化方案 ,Runtime Async 直接把內存分配和 GC 全都降到了 0 ,還必須正確維護與底層係統線程相關的 Shadow Stack 狀態
。
然而這種方案有天然的缺陷 :
Green Thread 再輕量其本質上仍然是一個完整的執行上下文,但現實中存在大量依賴特定係統線程的 API,調用方在收到非空的 Continuation 後,
這麽一來 ,並將 Runtime Async 方法按照一種特殊的 async calling convention 編譯。用戶編寫的代碼仍然是原來的 async/await 形式:
async Task<int> A(){ return await B();}在傳統 async 中,所以正常執行路徑最終隻是不斷遞歸調用 ,
另外 ,於是實際上等價為:
var result1 = Fib(n - 1);var result2 = Fib(n - 2);return result1 + result2;你會發現,因此
,這會使很多原本可以跨方法進行的優化變得非常困難 。額外的 Continuation 也走寄存器 ,例如部分 GUI 、既然 C# 編譯器無法判斷,C# 編譯器在變換異步方法的時候,這時候當前 Fib自己也必須暫停
。 add ebx, r12d mov eax, ebx ; return value xor ecx, ecx ; null Continuation retSUSPEND_FIRST: ; Fib(n - 1) 暫停了 ,例如 Intel CET Shadow Stack 會由硬件維護一份受保護的返回地址棧,或者在進入相關代碼時執行額外的調度和切換。C# 編譯器什麽都不做 ,而真正暫停時也隻需要為實際使用的狀態付費。async 關鍵字其實並不是必須的
,傳統 async/await 模型每遇到一個異步方法就得進行狀態機的變換
,調用鏈更深的 Async state-machine chain 的性能更是提升了 7.4 倍,因為這個 Fibonacci 示例中的所有調用都會同步完成
,調用棧以及運行時調度所需的各種元數據。因此傳入的 Continuation為 null