Parallel.ForEach比ForEach慢

这是代码:

using (var context = new AventureWorksDataContext()) { IEnumerable _customerQuery = from c in context.Customers where c.FirstName.StartsWith("A") select c; var watch = new Stopwatch(); watch.Start(); var result = Parallel.ForEach(_customerQuery, c => Console.WriteLine(c.FirstName)); watch.Stop(); Debug.WriteLine(watch.ElapsedMilliseconds); watch = new Stopwatch(); watch.Start(); foreach (var customer in _customerQuery) { Console.WriteLine(customer.FirstName); } watch.Stop(); Debug.WriteLine(watch.ElapsedMilliseconds); } 

问题是, Parallel.ForEach比常规foreach大约需要400ms,大约需要40ms。 我究竟做错了什么,为什么这不能像我期望的那样工作?

假设您有一个要执行的任务。 假设你是一名数学老师,你有二十篇论文要评分。 你需要花两分钟来评分一篇论文,所以你需要大约四十分钟。

现在让我们假设您决定聘请一些助理来帮助您评分论文。 你需要一个小时来找到四个助手。 你们各自拿了四篇论文,你们都在八分钟内完成了。 你已经交易了40分钟的工作,总共68分钟的工作,包括额外的一小时找工作,所以这不是节省。 寻找助手的开销大于自己完成工作的成本。

现在假设你有两万篇论文要评分,所以你需要大约40000分钟。 现在如果你花一个小时寻找助手,那就是胜利。 你们各自拿了4000篇论文,总共8060分钟而不是40000分钟,节省了近5倍。找到助手的开销基本上是无关紧要的。

并行化不是免费的与每个线程完成的工作量相比,在不同线程之间拆分工作的成本需要很小。

进一步阅读:

https://en.wikipedia.org/wiki/Amdahl%27s_law

https://en.wikipedia.org/wiki/Gustafson%27s_law

你应该意识到的第一件事是并非所有的并行都是有益的。 并行性存在大量开销,并且这种开销可能会或可能不会很大,这取决于并行化的复杂性。 由于并行function中的工作量非常小,因此并行性所需的管理开销变得非常大,从而减慢了整体工作。

为可枚举的VS创建所有线程只是执行可数的额外开销很可能是减速的原因。 Parallel.ForEach不是一揽子性能提升的举措; 无论是否可能阻止要为每个元素完成的操作,都需要对其进行权衡。

例如,如果您要创建Web请求或其他内容而不是简单地写入控制台,则并行版本可能会更快。 实际上,简单地写入控制台是一个非常快速的操作,因此创建线程并启动它们的开销会变慢。

正如之前的作者所说, Parallel.ForEach存在一些开销,但这并不是你无法看到性能提升的原因。 Console.WriteLine是一个同步操作,因此一次只能运行一个线程。 尝试将身体更改为非阻塞状态,您将看到性能提升(只要身体中的工作量足以超过开销)。