如何通过Parallel.ForEach实现最大并行度并利用最大CPU?

时间:2016-07-07 10:43:26

标签: c# task-parallel-library cpu-usage parallel.foreach

有一个C#函数A(arg1, arg2)需要多次调用。为了做到这一点,我正在使用并行编程。

以下面的代码为例:

long totalCalls = 2000000;
int threads = Environment.ProcessorCount;

ParallelOptions options = new ParallelOptions(); 
options.MaxDegreeOfParallelism = threads;

Parallel.ForEach(Enumerable.Range(1, threads), options, range =>
{
    for (int i = 0; i < total / threads; i++)
    {
        // init arg1 and arg2
        var value = A(arg1, agr2);
        // do something with value
    }
});

现在的问题是,随着核心数量的增加,这不会扩大规模;例如在8个内核上,它使用80%的CPU,在16个内核上,它使用40-50%的CPU。我想最大限度地使用CPU。

您可能认为A(arg1, arg2)内部包含复杂的计算,但它没有任何IO或网络绑定操作,也没有线程锁定。有哪些其他可能性可以找出代码的哪一部分使其不能以100%并行的方式执行?

我也尝试过提高并行度,例如

int threads = Environment.ProcessorCount * 2;
// AND
int threads = Environment.ProcessorCount * 4;
// etc.

但它没有任何帮助。

更新1 - 如果我通过使用计算素数的简单函数替换A()来运行相同的代码,那么它将使用100个CPU并且可以很好地扩展。所以这证明了其他代码是正确的。现在问题可能在原始函数A()内。我需要一种方法来检测导致某种排序的问题。

1 个答案:

答案 0 :(得分:6)

您已确定A中的代码是问题所在。

有一个非常常见的问题:垃圾收集。在app.config中配置您的应用程序以使用并发服务器GC。 Workstation GC倾向于序列化执行。效果很严重。

如果这不是问题,请暂停调试器几次并查看Debug -> Parallel Stacks窗口。在那里,你可以看到你的线程在做什么。寻找共同的资源和争用。例如,如果您发现许多线程正在等待锁定您的问题。

另一个不错的调试技术是注释掉代码。一旦可伸缩性限制消失,您就会知道导致它的代码。

相关问题