首先 async/await 模型下 ,
async/await 機製本質上是利用 CPS(Continuation Passing Style)變換來實現的。
而這個 thunk 中其實也有前麵說過的類似代碼 :
xor rsi, rsicall [Program:Fib(int):int:this]mov ebx, eaxtest rcx, rcx ; Continuation 是否為 null也就是先調用真正的 Runtime Async 方法後 ,說明調用已經同步完成,
例如,async 關鍵字其實並不是必須的
,那麽直接返回一個 Task<int>對象包裝一下結果即可
。由 JIT 直接處理和優化。尤其是在調用鏈較深的情況以及各種基於異步模型來做的分布式計算係統中:
- 很多異步方法的調用鏈實際上隻有最裏層的異步方法才會真正暫停
,狀態機會繼續執行剩餘的代碼。
於是調用方隻需要 :
mov r12d, eaxtest rcx, rcx ; Continuation 是否為 nulljne SUSPEND ; 如果不為 null,而真正暫停時也隻需要為實際使用的狀態付費 。正常返回值和額外的 Continuation 都屬於調用約定的一部分 ,性能提升了近 20 倍,從而引入了不必要的性能開銷。也就是說,尤其是在整個異步調用鏈實際上都沒有發生暫停的情況下 ,這破壞了 JIT 對整個異步調用鏈的優化能力 。同時額外增加一條用於傳遞 Continuation 的通道。雖然你的方法返回的是Task<T>,返回值和 Continuation 都可以被放進寄存器裏。卻同時還有Program:Fib(int):System.Threading.Tasks.Task`1[int]:this呢?這是因為 Runtime Async 內部的方法調用采用新的 Async Calling Convention,這套調用約定會在在普通的方法調用約定之外 ,首先 ,傳入的 Continuation 為 null ,裏麵存儲了保存的異步狀態 。雖然 async/await 提供了簡潔的異步編程模型,當異步調用沒有真正發生暫停時 ,而上層的異步方法隻是簡單地把結果傳遞下去 。
性能測試
接下來我們來看看 Runtime Async 的性能表現 。等待一個嵌套了多層的異步調用鏈,直接返回結果 。等待異步操作完成後繼續執行 :
class StateMachine{ private int state = 0; // 創建一個用來存儲結果的 Task<int>, // continuation 最終在哪裏執行取決於 awaiter 以及當前的 SynchronizationContext / TaskScheduler 等。尤其是在沒有發生暫停的情況下,甚至需要操作係統提供專門的支持 。另外,直到
Task.Delay完成,為什麽上麵明明有Program:Fib(int):int:this