簡體   English   中英

使用 async-await 可以為您帶來任何性能優勢嗎?

[英]Can using async-await give you any performance benefits?

每當我讀到async - await時,用例示例總是存在您不想凍結的 UI。 要么所有編程書籍/教程都相同,要么 UI 阻塞是async的唯一情況 - 作為開發人員我應該知道的await

是否有任何示例說明如何使用async - await在算法中獲得性能優勢? 就像讓我們來回答任何經典的編程面試問題:

  • 在二叉樹中找到最近的共同祖先
  • 給定a[0] , a[1] , ..., a[n-1]表示以 10 為基數的數字,找到使用相同數字的下一個最高數字
  • 找到兩個排序的 arrays 的中位數(即,如果要合並它們的中位數)
  • 給定一個數字數組1 , 2 , ..., n缺少一個數字,找到缺少的數字
  • 查找數組中最大的 2 個數字

有什么方法可以使用async - await來獲得性能優勢? 如果是這樣,如果您只有 1 個處理器怎么辦? 那么你的機器不是只是在任務之間分配時間,而不是真正同時執行它們嗎?

這次采訪中,埃里克·利珀特(Eric Lippert)比較了異步等待與做早餐的廚師 它幫助我理解了async-await的好處。 在中間某處搜索'async-await'

假設廚師必須做早餐。 他必須烤面包,煮一些雞蛋,也可以煮一些茶?

方法1:同步。 由一個線程執行 你開始烘烤面包。 等到面包烤好了。 取出面包。 開始加水,等到水沸騰,然后插入雞蛋。 等到雞蛋准備好然后取出雞蛋。 開始為茶喝水。 等到水煮沸后再煮茶。

你會看到所有的等待。 當線程在等待它可以做其他事情。

方法2:Async-await,仍然是一個線程你開始烘烤面包。 當面包被烤時,你開始為雞蛋和茶開水。 然后你開始等待。 完成三個任務中的任何一個后,您將完成任務的第二部分,具體取決於首先完成的任務。 因此,如果雞蛋的水首先煮沸,你煮雞蛋,並再次等待任何任務完成。

在這個描述中,只有一個人(你)正在做所有的事情。 只涉及一個線程。 好的方面是,因為只有一個線程在執行這些操作,所以代碼看起來與讀者非常同步,並且沒有太多需要使您的變量線程安全。

很容易看出,這樣你的早餐將在更短的時間內准備好(你的面包仍然會很溫暖!)。 在計算機生活中,當線程必須等待另一個進程完成時,這些事情就會發生,例如將文件寫入磁盤,從數據庫或從Internet獲取信息。 這些函數通常會看到函數的異步版本: WriteWriteAsyncReadReadAsync

另外:在其他用戶的一些評論和一些測試之后,我發現事實上它可以是任何線程在等待之后繼續你的工作。 這個其他線程具有相同的“上下文”,因此可以表現為它是原始線程。

方法3:雇佣廚師烘烤面包,煮茶時煮雞蛋:真正異步。 幾個線程這是最昂貴的選項,因為它涉及創建單獨的線程。 在制作早餐的例子中,這可能不會非常加快這個過程,因為相對較長的過程你無論如何都沒有做任何事情。 但是,例如,如果您還需要切片番茄,那么當您使用async-await執行其他操作時,讓烹飪(單獨的線程)執行此操作可能會很方便。 當然,你做的其中一個等待廚師完成他的切片。

另一篇解釋很多的文章是Async和Await寫的那些有用的Stephen Cleary。

每當我讀到async-await時,用例示例總是存在一個您不想凍結的UI。

這是async的最常見用例。 另一個是服務器端應用程序,其中async可以提高Web服務器的可伸縮性。

是否有任何示例可以使用async-await來優化算法中的性能優勢?

沒有。

如果要進行並行處理,可以使用任務並行庫。 並行處理是使用多個線程,在系統中的多個核心之間划分算法的部分。 並行處理是一種並發形式(同時執行多個操作)。

異步代碼完全不同。 異步代碼的要點是在操作進行時使用當前線程。 異步代碼通常是I / O綁定或基於事件(如計時器)。 異步代碼是另一種並發形式。

我在我的博客上有一個async介紹 ,以及關於async如何不使用線程的帖子。

請注意,任務並行庫使用的任務可以調度到線程上並執行代碼。 基於任務的異步模式使用的任務沒有代碼,也沒有“執行”。 雖然兩種類型的任務都由相同的類型( Task )表示,但它們的創建和使用方式完全不同; 我在我的博客上更詳細地描述了這些委托任務和承諾任務

簡而言之,非常一般 - 不,它通常不會。 但它需要更多的單詞,因為“性能”可以通過多種方式理解。

僅當“作業”受I / O限制時,Async / await才會“節省時間”。 將它應用於受CPU限制的作業會引入一些性能命中。 那是因為如果你有一些計算,即你的CPU需要10秒,那么添加async / await - 即:任務創建,調度和同步 - 只需將X額外的時間添加到你需要刻錄的10秒內你的CPU完成工作。 接近阿姆達爾定律的想法。 不是真的,但非常接近。

然而,有一些'但是..'。

首先,由於引入異步/等待而導致的性能下降並不是那么大。 (特別是如果你小心不要過頭)。

其次,由於async / await允許您更容易地編寫I / O交錯代碼,您可能會注意到在您懶得(:)的地方刪除I / O的等待時間的新機會,否則或者在沒有異步/等待語法良好的情況下使代碼難以遵循的地方。 例如,圍繞網絡請求拆分代碼是相當明顯的事情,但您可能會注意到,即您還可以在您編寫CSV文件或讀取配置文件等的少數幾個位置升級某些文件i / o。這里的增益不會歸功於async / await - 這要歸功於重寫處理文件i / o的代碼。 你可以在沒有異步/等待的情況下做到這一點。

第三,由於某些i / o操作更容易,您可能會注意到將CPU密集型工作卸載到另一個服務或機器上要容易得多,這也可以提高您的感知性能(縮短“掛鍾”時間),但整體而言資源消耗將增加:添加另一台機器,花費在網絡操作上的時間等。

第四:UI。 你真的不想凍結它。 在Tasks和async / await中包裝I / O綁定和CPU綁定作業非常容易,並保持UI響應。 這就是為什么你看到它隨處可見。 但是,雖然理想情況下I / O綁定操作應該是異步,但要在所有冗長的I / O上移除盡可能多的空閑等待時間,但CPU綁定的作業不需要分割或異步化而不僅僅是1水平下降。 將巨大的單片計算工作包含在一個任務中就足以讓UI解除阻塞。 當然,如果你有許多處理器/核心,它仍然值得並行化內部的任何可能,但與I / O相反 - 分裂太多,你將忙於切換任務而不是咀嚼計算。

總結:如果你有時間進行I / O - 異步操作可以節省很多時間。 很難過度異步I / O操作。 如果您有CPU占用操作,那么添加任何東西將消耗更多的CPU時間和更多的內存,但由於將作業分成更小的部分,可以在同一個更多的內核上運行,因此掛鍾時間可以更好時間。 過度使用並不難,所以你需要小心一點。

大多數情況下,你沒有獲得直接的性能(你正在執行的任務發生得更快和/或內存更少),就像在可擴展性方面一樣; 使用較少的線程來執行相同數量的同時任務意味着您可以執行的同時任務數量更高。

因此,在大多數情況下,您沒有發現某項操作在性能方面有所改進,但可以發現大量使用會提高性能。

如果操作需要涉及真正異步(多個異步I / O)的並行任務,那么該可伸縮性可以使單個操作受益。 由於線程中發生阻塞的程度會降低,即使您只有一個核心,也會發生這種情況,因為機器只將時間分配給當前沒有等待的任務。

這與並行CPU綁定操作不同(無論是使用任務還是以其他方式完成)通常只會擴展到可用的核心數。 (在某些方面,超線程內核的行為類似於2個或更多內核,而在其他方面則不然。

該方法在當前同步上下文上運行,並僅在方法處於活動狀態時在線程上使用時間。 您可以使用Task.Run將CPU綁定的工作移動到后台線程,但后台線程無助於只等待結果可用的進程。

當應用程序中有一個CPU和多個線程時,CPU會在線程之間切換以模擬並行處理。 使用async / await,您的異步操作不需要線程時間,因此您可以為應用程序的其他線程提供更多時間來完成工作。 例如,您的應用程序(非UI)仍然可以進行HTTP調用,您只需等待響應即可。 這是使用async / await的好處很大的情況之一。

當您調用async DoJobAsync()不要忘記async DoJobAsync() .ConfigureAwait(false)以獲得不需要合並回UI線程上下文的非UI應用程序的更好性能。

我沒有提到很好的語法,這有助於保持代碼清潔。

MSDN

async和await關鍵字不會導致創建其他線程。 異步方法不需要多線程,因為異步方法不能在自己的線程上運行。 該方法在當前同步上下文上運行,並僅在方法處於活動狀態時在線程上使用時間。 您可以使用Task.Run將CPU綁定的工作移動到后台線程,但后台線程無助於只等待結果可用的進程。

.NET的async-await功能與其他框架的功能沒有什么不同。 它沒有在本地計算中提供性能優勢,但它只允許在單個線程中的任務之間連續切換,而不是讓一個任務阻塞線程。 如果要獲得本地計算的性能增益,請使用任務並行庫。

訪問https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

async使您的應用程序更具響應性,但它確實帶來了很小的性能開銷。

一個真實世界的例子:

We just rewrote a huge and heavily used ASP.NET Core app to be fully async "all the way down" (in places where it makes sense - I/O, external API requests over http, email protocols like SMTP/IMAP etc.), following Stephen Cleary 的博客/書籍中的所有最佳實踐和結果是:

優點:

  1. 該應用程序變得更加靈敏,確實減少了阻塞
  2. 該應用程序使用更少的線程(從而保護自己不會最大化線程池並防止在非常重的負載下“線程飢餓”)

缺點:

  1. 進程的平均 CPU 負載增加了 3-4%(從 5-6% 到 8-10%)

分析應用程序后,大部分 CPU 周期(約 15%)用於調度任務、延續和保存/恢復 state。 內部低級 TPL 東西,如RunOrScheduleAction IAsyncStateMachineBox等。

雖然是 YMMV。 衡量一切

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM