【问题标题】:Performance penalty between for loop and Parallel.For() with MaxDegreeOfParallelism of 1MaxDegreeOfParallelism 为 1 的 for 循环和 Parallel.For() 之间的性能损失
【发布时间】:2014-10-03 05:18:22
【问题描述】:

我想做如下的事情:

int firstLoopMaxThreads = 1; // or -1
int secondLoopMaxThreads = firstLoopMaxThreads == 1 ? -1 : 1;

Parallel.For(0, m, new ParallelOptions() { MaxDegreeOfParallelism = firstLoopMaxThreads }, i =>
{
    //do some processor and/or memory-intensive stuff
    Parallel.For(0, n, new ParallelOptions() { MaxDegreeOfParallelism = secondLoopMaxThreads }, j =>
    {
        //do some other processor and/or memory-intensive stuff
    });
});

在性能方面,当 secondLoopMaxThreads = 1 时,将内部 Parallel.For 循环与普通 for 循环交换是否值得?常规 for 循环和 MaxDegreeofParallelism = 1 的 Parallel.For 循环之间的性能差异是什么?

【问题讨论】:

  • AFAIK 唯一的区别是 Parallel.For 循环将被安排在线程池线程上运行(这可能会产生一些调度开销)。除此之外,循环体在两种情况下都应该表现得同样好。 ICBWT。
  • 旁注:请务必查看 Eric Lippert 的 Which is faster,以使 [性能] 问题变得更好......
  • 它是否值得主要取决于您在这种情况下对性能的关心程度。这不是我们可以告诉你的。不同的性能可能足够重要,也可能不重要。只有你能回答这个问题。

标签: c# performance task-parallel-library


【解决方案1】:

这取决于您谈论的迭代次数以及您谈论的性能水平来回答是否值得。在您的上下文中,1ms 被认为是多还是少?

我进行了如下初步测试(因为 Thread.Sleep 并不完全准确.. 虽然 for 循环每次测量 15,000 毫秒到 1 毫秒以内)。超过 15,000 次迭代重复了 5 次,与标准 for 循环相比,它通常会增加大约 4 毫秒的开销……但当然结果会因环境而异。

for (int z = 0; z < 5; z++)
{
    int iterations = 15000;

    Stopwatch s = Stopwatch.StartNew();

    for (int i = 0; i < iterations; i++)        
        Thread.Sleep(1);        

    s.Stop();

    Console.WriteLine("#{0}:Elapsed (for): {1:#,0}ms", z, ((double)s.ElapsedTicks / (double)Stopwatch.Frequency) * 1000);

    var options = new ParallelOptions() { MaxDegreeOfParallelism = 1 };
    s = Stopwatch.StartNew();

    Parallel.For(0, iterations, options, (i) => Thread.Sleep(1));

    s.Stop();

    Console.WriteLine("#{0}: Elapsed (parallel): {1:#,0}ms", z, ((double)s.ElapsedTicks / (double)Stopwatch.Frequency) * 1000);
}

【讨论】:

  • 睡眠精确到系统计时器的分辨率。您可能正在运行 Chrome 或将分辨率设置为 1 毫秒的媒体播放器。也就是说,这个基准有一些东西会扭曲结果。 1ms 的开销是极端的。不要使用睡眠。
  • 我没有说任何东西有 1ms 的开销.. 不知道你指的是什么.. 我的意思是如果我尝试睡 1ms 15,000 次并且循环需要 15,000ms总共在 1 毫秒内,睡眠相当稳定。
  • 啊,我理解你的最后一段说每个项目增加了 4 毫秒的开销以进行 5 次循环运行。好的,现在一切都说得通了。
【解决方案2】:

body 循环在两个版本中的表现都一样好,但循环本身在使用Parallel.For 时要慢得多,即使对于单线程执行也是如此。每个元素都需要调用一个委托。这比递增循环计数器要慢得多。

如果你的循环体做了任何有意义的事情,那么有用的工作就会使循环开销相形见绌。只要确保您的工作项不会太小,您就不会注意到差异。

嵌套并行循环很少是一个好主意。如果工作项既不太小也不太大,通常一个并行循环就足够了。

【讨论】:

  • 嵌套并行循环几乎从来都不是一个好主意,我同意。我特别设置了示例代码,使得两个循环永远不会同时运行,尽管只有一个或另一个。您是否仍然认为这样的设置是一个坏主意,如果是,为什么?
  • 在这种情况下,这不是一个坏主意。您遭受并行循环的单线程开销,仅此而已。让您的工作项目更大,您无需担心开销。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-20
  • 1970-01-01
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多