[英]Performances of PLINQ vs TPL
我有一些數據庫操作要執行,我嘗試使用PLINQ :
someCollection.AsParallel()
.WithCancellation(token)
.ForAll(element => ExecuteDbOperation(element))
而且我發現與以下內容相比,它的速度相當慢:
var tasks = someCollection.Select(element =>
Task.Run(() => ExecuteDbOperation(element), token))
.ToList()
await Task.WhenAll(tasks)
我更喜歡PLINQ語法,但是我不得不使用第二個版本進行演奏。
有人可以解釋一下演出的巨大差異嗎?
如果您說多於10000個元素,那我相信,最好使用PLINQ,因為它不會為集合的每個元素創建任務,因為它在其中使用了Partitioner。 每個任務創建內部都有一些開銷數據初始化。 分區程序只會創建為當前可用內核優化的盡可能多的任務,因此它將重新使用此任務以及新數據進行處理。 您可以在此處了解更多信息: http : //blogs.msdn.com/b/pfxteam/archive/2009/05/28/9648672.aspx
我認為這是由於創建的線程數所致。
在第一個示例中,該數字將大致等於計算機的內核數。 相比之下,第二個示例將創建與someCollection
具有元素一樣多的線程。 對於IO操作,通常效率更高。
Microsoft指南“ Patterns_of_Parallel_Programming_CSharp”建議IO操作創建比默認更多的線程(第33頁):
var addrs = new[] { addr1, addr2, ..., addrN };
var pings = from addr in addrs.AsParallel().WithDegreeOfParallelism(16)
select new Ping().Send(addr);
PLINQ和Parallel.ForEach()
都主要用於處理受CPU約束的工作負載,這就是為什么它們在IO約束工作中表現不佳的原因。 對於某些特定的IO綁定工作,有一個最佳的並行度,但它並不取決於CPU內核的數量,而PLINQ和Parallel.ForEach()
中的Parallel.ForEach()
度卻取決於CPU內核的數量,或大或小。
具體來說,PLINQ的工作方式是使用固定數量的Task
,默認情況下根據您計算機上CPU內核的數量。 這對於一系列的PLINQ方法來說很有效。 但似乎這個數字小於您工作的理想並行度。
另一方面, Parallel.ForEach()
代表確定要運行多少Task
到ThreadPool
。 而且,只要其線程被阻塞, ThreadPool
慢慢添加它們。 結果是,隨着時間的流逝, Parallel.ForEach()
可能會更接近理想的並行度。
正確的解決方案是通過測量,然后使用它來找出適合您的工作的並行度。
理想情況下,您將使代碼異步,然后使用某種方法來限制async
代碼的並行度 。
既然您說還不能做到這一點,我認為一個不錯的解決方案可能是避免ThreadPool
並在專用線程上運行您的工作(可以通過將Task.Factory.StartNew()
與TaskCreationOptions.LongRunning
一起使用來創建線程) 。
如果您可以堅持使用ThreadPool
,那么另一個解決方案是使用PLINQ ForAll()
,還可以調用WithDegreeOfParallelism()
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.