[英]Parallel.ForEach performance
我正在使用Parallel.ForEach
提取一堆壓縮文件,並將它們復制到另一台計算機上的共享文件夾中,然后在該文件夾中開始BULK INSERT
過程。 這一切都很好,但是我注意到,一旦出現一些大文件,就不會啟動新任務。 我認為這是因為某些文件比其他文件花費的時間更長,TPL開始按比例縮小,並停止創建新的任務。 我已經將MaxDegreeOfParallelism
設置為一個合理的數字(8)。 當我查看CPU活動時,我可以看到,大多數情況下SQL Server計算機低於30%,而在執行單個BULK INSERT
任務時則更少。 我認為它可以做更多的工作。 我可以以某種方式強制TPL創建更多同時處理的任務嗎?
原因很可能是默認情況下Parallel.ForEach
處理項目的方式。 如果在數組或實現IList
上使用它(這樣就可以使用總長度和索引器),它將分批拆分整個工作負載。 然后,單獨的線程將處理每個批次。 這意味着如果批次具有不同的“大小”(按大小,我是指處理它們的時間)-“小”批次將更快地完成。
例如,讓我們看下面的代碼:
var delays = Enumerable.Repeat(100, 24).Concat(Enumerable.Repeat(2000, 4)).ToArray();
Parallel.ForEach(delays, new ParallelOptions() {MaxDegreeOfParallelism = 4}, d =>
{
Thread.Sleep(d);
Console.WriteLine("Done with " + d);
});
如果運行它,您將看到所有“ 100”(快速)項目都被快速並行處理。 但是,所有“ 2000”(慢)項目最終都將一一處理,而無需任何並行處理。 這是因為所有“慢”項目都在同一批次中。 工作負載分為4個批次( MaxDegreeOfParallelism = 4
),並且前3個僅包含快速項目。 他們完成得很快。 最后一批具有所有慢速項目,因此專用於此批處理的線程將逐個處理它們。
您可以通過確保項目均勻分配(以使“慢”項目在源集合中不全部在一起)來“修復”您的情況,例如使用自定義分區程序:
var delays = Enumerable.Repeat(100, 24).Concat(Enumerable.Repeat(2000, 4)).ToArray();
var partitioner = Partitioner.Create(delays, EnumerablePartitionerOptions.NoBuffering);
Parallel.ForEach(partitioner, new ParallelOptions {MaxDegreeOfParallelism = 4}, d =>
{
Thread.Sleep(d);
Console.WriteLine("Done with " + d);
});
NoBuffering
可以確保一次提取一項,從而避免了該問題。
也可以使用其他方式來並行化工作(例如SemaphoreSlim
或BlockingCollection
)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.