如何实现最大并行度并利用最大 CPU 和 Parallel.ForEach?
How can I achieve maximum parallelism and utilize maximum CPU with 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 个内核上,它使用了 CPU 的 80%,而在 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()
中。我需要一种方法来检测导致某种排序的问题。
您已确定 A
中的代码是问题所在。
有一个非常普遍的问题:垃圾回收。在 app.config
中配置您的应用程序以使用并发服务器 GC。 Workstation GC 倾向于序列化执行。效果很严重。
如果这不是问题,请暂停调试器几次并查看 Debug -> Parallel Stacks
window。在那里,您可以看到您的线程在做什么。寻找共同的资源和竞争。例如,如果您发现许多线程在等待锁,那就是您的问题。
另一个不错的调试技巧是注释掉代码。一旦可伸缩性限制消失,您就会知道是什么代码导致的。
有一个 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 个内核上,它使用了 CPU 的 80%,而在 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()
中。我需要一种方法来检测导致某种排序的问题。
您已确定 A
中的代码是问题所在。
有一个非常普遍的问题:垃圾回收。在 app.config
中配置您的应用程序以使用并发服务器 GC。 Workstation GC 倾向于序列化执行。效果很严重。
如果这不是问题,请暂停调试器几次并查看 Debug -> Parallel Stacks
window。在那里,您可以看到您的线程在做什么。寻找共同的资源和竞争。例如,如果您发现许多线程在等待锁,那就是您的问题。
另一个不错的调试技巧是注释掉代码。一旦可伸缩性限制消失,您就会知道是什么代码导致的。