36

这是代码:

using (var context = new AventureWorksDataContext())
{
    IEnumerable<Customer> _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需要 400 毫秒,而常规大约需要 40 毫秒。我到底做错了什么,为什么这不能像我预期的那样工作?

4

5 回答 5

188

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

现在让我们假设您决定聘请一些助手来帮助您对论文进行评分。您需要一个小时才能找到四个助手。你们每人拿四张纸,八分钟内就完成了。您已经用 40 分钟的工作换取了总共 68 分钟的工作,包括额外的一小时寻找助手,所以这不是节省。寻找助手的开销大于自己做工作的成本。

现在假设你有两万篇论文要评分,那么这将花费你大约 40000 分钟。现在,如果您花一个小时寻找助手,那将是一场胜利。你们每人拿了 4000 篇论文,总共用了 8060 分钟而不是 40000 分钟,节省了几乎 5 倍。寻找助手的开销基本上是无关紧要的。

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

进一步阅读:

阿姆达尔定律

给出了在固定工作负载下执行任务的延迟的理论加速,这对于资源得到改进的系统来说是可以预期的。

古斯塔夫森定律

给出了在固定执行时间执行任务的延迟的理论加速,这对于资源得到改进的系统来说是可以预期的。

于 2011-05-17T20:20:31.323 回答
12

您应该意识到的第一件事是,并非所有并行性都是有益的。并行性有一定的开销,根据并行化的复杂性,这种开销可能很大也可能不显着。由于并行函数中的工作非常小,并行性必须执行的管理开销变得很大,从而减慢了整体工作的速度。

于 2011-05-17T19:36:42.313 回答
10

为仅执行可枚举的 VS 创建所有线程的额外开销很可能是导致速度下降的原因。Parallel.ForEach不是一揽子提高业绩的举措;需要权衡每个元素要完成的操作是否可能阻塞。

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

于 2011-05-17T19:37:27.180 回答
6

正如之前的作者所说,有一些与 相关的开销Parallel.ForEach,但这不是您看不到性能改进的原因。Console.WriteLine是同步操作,因此一次只有一个线程在工作。尝试将 body 更改为非阻塞的东西,您会看到性能提升(只要 body 的工作量大到足以超过开销)。

于 2013-02-26T11:14:43.920 回答
1

我喜欢所罗门的回答,并想补充一点,您还有额外的开销

  1. 分配代表。
  2. 通过他们打电话。
于 2018-12-11T10:04:32.987 回答