【问题标题】:Why is this parallel code slower than its similar non parallel version?为什么这个并行代码比它类似的非并行版本慢?
【发布时间】:2016-01-09 06:24:19
【问题描述】:

我有以下代码(从 LINQPad 复制到此处)。显然我不明白 TPL 是如何工作的,或者代码是垃圾,为什么并行版本的运行速度比非并行版本慢?

for (int i = 0; i < 100; i++)
{
    ParallelOptions ops = new ParallelOptions();
    ops.MaxDegreeOfParallelism = Environment.ProcessorCount;

    var watch = Stopwatch.StartNew();
    Parallel.ForEach<int>(Enumerable.Range(1, 10000000), ops, x => { int y = x + 1; });
    watch.Stop();
    Console.WriteLine("Parallel: {0}", watch.Elapsed.TotalSeconds);

    watch = Stopwatch.StartNew();
    foreach (var x in Enumerable.Range(1, 10000000))
    {
        int y = x + 1;
    }
    watch.Stop();
    Console.WriteLine("Non-parallel: {0}\n", watch.Elapsed.TotalSeconds);
}

前 10 个结果:

平行:0.1991644 非平行:0.0466178

平行:0.1723428 非平行:0.0447134

平行:0.1141791 非平行:0.0444557

平行:0.1758878 非平行:0.0444636

平行:0.1687637 非平行:0.0444338

平行:0.1677679 非平行:0.0445771

平行:0.1191462 非平行:0.0446116

平行:0.1702483 非平行:0.0454863

平行:0.1143605 非平行:0.0451731

平行:0.2155218 非平行:0.0450392

【问题讨论】:

  • 启动线程并非没有成本
  • 调试模式编译?因为当优化时,代码什么都不做,甚至可能被优化掉。确定哪个更快没有意义:什么都不做或启动线程什么都不做。
  • 线程非常昂贵;只有当您有大量工作要做时,雇用工人才有意义。
  • 看看这个SO link接受的答案

标签: c# parallel-processing task-parallel-library task


【解决方案1】:

嗯,您可以获得的最佳答案是运行分析器工具并测量您的代码发生了什么。但我有根据的猜测是,您的并行代码速度较慢,因为您的代码非常简单,以至于启动线程和在它们之间切换会增加如此多的成本,以至于计算速度的任何优势都可以忽略不计。

但是尝试进行一些实质性的计算,您最终将获得并行执行的优势。你的代码太简单了。现代 CPU 不能以这种方式加载。

【讨论】:

    【解决方案2】:

    由于我无法将此添加为评论,因此我正在添加另一个答案以发布修改后的代码。 @ixSci 在他的回答中所说的似乎是正确的。我在并行代码体中执行了一个微不足道的操作,该代码执行得非常快,但速度慢是因为在线程之间的上下文切换上花费了大量时间?当我将代码更改为休眠一段时间而不是将 int 值增加 1 时,并行代码比非并行版本快大约 4(我的 CPU 中的内核数)倍。

    for (int i = 0; i < 100; i++)
    {
        ParallelOptions ops = new ParallelOptions();
        ops.MaxDegreeOfParallelism = Environment.ProcessorCount;
    
        var partitioner = Partitioner.Create<int>(Enumerable.Range(1, 5000));
    
        var watch = Stopwatch.StartNew();
        Parallel.ForEach<int>(partitioner, ops, x => { Thread.Sleep(1); });
        watch.Stop();
        Console.WriteLine("Parallel: {0}", watch.Elapsed.TotalSeconds);
    
        watch = Stopwatch.StartNew();
        foreach (var x in Enumerable.Range(1, 5000))
        {
            Thread.Sleep(1);
        }
        watch.Stop();
        Console.WriteLine("Non-parallel: {0}\n", watch.Elapsed.TotalSeconds);
    }
    

    前 10 个结果:

    并行:1.2887589 非并行:5.0020569

    并行:1.277047 非并行:5.0011116

    并行:1.2790631 非并行:5.0001498

    并行:1.2770644 非并行:5.0052016

    并行:1.2770013 非并行:5.0021479

    并行:1.2770031 非并行:5.0001927

    并行:1.2799937 非并行:5.0062141

    并行:1.2819909 非并行:5.0171945

    并行:1.2780496 非并行:5.0071667

    并行:1.2821714 非并行:5.0082108

    并行:1.2777875 非并行:5.0152099

    【讨论】:

    • 这也不是一个很好的例子。如果您和 ThreadPool 停止限制使用的线程数(通过不强制 MaxDegreeOfParallelismProcessorCount 并添加类似 ThreadPool.SetMinThreads(100, 4); 的内容),那么您将获得比CPU 核心数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多