C# 对于昂贵的IEnumerables,多线程程序不使用100%CPU
使用IEnumerables进行多线程处理时,不使用100%的CPU,IEnumerables并行计算多次,计算成本很高。示例是与Concat()组合的Aggregate()函数:C# 对于昂贵的IEnumerables,多线程程序不使用100%CPU,c#,.net,multithreading,ienumerable,cpu-usage,C#,.net,Multithreading,Ienumerable,Cpu Usage,使用IEnumerables进行多线程处理时,不使用100%的CPU,IEnumerables并行计算多次,计算成本很高。示例是与Concat()组合的Aggregate()函数: 您的线程在clr.dll上被阻塞的事实!WKS::gc_heap::wait_for_gc_done表明垃圾收集器是应用程序的瓶颈。您应该尽可能地限制程序中堆分配的数量,以减少gc的压力 也就是说,还有另一种方法可以加快速度。默认情况下,在桌面上,GC配置为使用计算机上的有限资源(以避免减慢其他应用程序的速度)。如果
您的线程在
clr.dll上被阻塞的事实!WKS::gc_heap::wait_for_gc_done
表明垃圾收集器是应用程序的瓶颈。您应该尽可能地限制程序中堆分配的数量,以减少gc的压力
也就是说,还有另一种方法可以加快速度。默认情况下,在桌面上,GC配置为使用计算机上的有限资源(以避免减慢其他应用程序的速度)。如果你想充分利用现有资源,那么你可以。此模式假定您的应用程序是计算机上运行的最重要的东西。它将提供显著的性能提升,但会使用更多的CPU和内存。为什么您认为它应该使用100%的CPU?10000个长度为1000000的字符串会消耗约20GB的内存。你能排除这个问题不是由于缺少可用物理内存而导致的页面错误吗?有趣的是,我无法从我的角度再现这个问题。NET 4.7.2,英特尔核心i7 5820K(12个vCore)。初始化非常慢,但是
并行。因为实际使用了95%的CPU,您确定这是正确的重新编程代码吗?您指示初始化-需要毫秒,我需要几分钟。您的最后一个方法GenerateLongString()看起来可能会大量使用GC。用CPU密集但没有太多分配的东西替换它。我认为正确的建议是使用StringBuilder,String.Concat()会产生太多垃圾。@HansPassantString.Concat(IEnumerable)
确实依赖StringBuilder。但是由于字符串的长度和数量是事先知道的,我想应该可以分配一个大小合适的StringBuilder来节省大量的分配。无论如何,在这个示例中有很多可能的优化,但我不知道OP试图运行的是“真正的”代码,还是仅仅是一个示例repro@HansPassant真正的代码没有String.Concat(),但它有许多IEnumerables操作(如GroupBy、OrderBy等)。repo代码只是最短的代码,我设法编写了它,它与原始代码有相同的问题。
// Initialisation.
// Each IEnumerable<string> is made so that it takes time to evaluate it
// everytime when it is accessed.
IEnumerable<string>[] iEnumerablesArray = ...
// The line of the question (using less than 100% CPU):
Parallel.For(0, 1000000, _ => iEnumerablesArray.Aggregate(Enumerable.Concat).ToList());
class Program
{
const int DATA_SIZE = 10000;
const int IENUMERABLE_COUNT = 10000;
static void Main(string[] args)
{
// initialisation - takes milliseconds
IEnumerable<string>[] iEnumerablesArray = GenerateArrayOfIEnumerables();
Console.WriteLine("Initialized");
List<string> result = null;
// =================
// THE PROBLEM LINE:
// =================
// CPU usage of next line:
// - 40 % on 4 virtual cores processor (2 physical)
// - 10 - 15 % on 12 virtual cores processor
Parallel.For(
0,
int.MaxValue,
(i) => result = iEnumerablesArray.Aggregate(Enumerable.Concat).ToList());
// just to be sure that Release mode would not omit some lines:
Console.WriteLine(result);
}
static IEnumerable<string>[] GenerateArrayOfIEnumerables()
{
return Enumerable
.Range(0, IENUMERABLE_COUNT)
.Select(_ => Enumerable.Range(0, 1).Select(__ => GenerateLongString()))
.ToArray();
}
static string GenerateLongString()
{
return string.Concat(Enumerable.Range(0, DATA_SIZE).Select(_ => "string_part"));
}
}