Ram*_*oni 8 c# cpu-usage task-parallel-library 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
}
});
Run Code Online (Sandbox Code Playgroud)
现在的问题是,随着核心数量的增加,这不会扩大规模; 例如,在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.
Run Code Online (Sandbox Code Playgroud)
但它没有任何帮助.
更新1 - 如果我通过替换A()一个计算素数的简单函数来运行相同的代码,那么它将使用100个CPU并且可以很好地扩展.所以这证明了其他代码是正确的.现在问题可能在原始函数内A().我需要一种方法来检测导致某种排序的问题.
您已确定代码A是问题所在.
有一个非常常见的问题:垃圾收集.配置您的应用程序app.config以使用并发服务器GC.Workstation GC倾向于序列化执行.效果很严重.
如果这不是问题,请暂停调试器几次并查看Debug -> Parallel Stacks窗口.在那里,你可以看到你的线程在做什么.寻找共同的资源和争用.例如,如果您发现许多线程等待锁定是您的问题.
另一个不错的调试技术是注释掉代码.一旦可伸缩性限制消失,您就会知道导致它的代码.