具有IEnumerables的多线程,并行评估几次并且评估成本高,不使用100%CPU。示例是与Concat()结合的Aggregate()函数:
// 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());
问题:为什么IEnumerables被并行评估多次的并行代码不使用100%CPU?代码不使用锁或等待,因此此行为是意外的。模拟这个的完整代码是在帖子的最后。
备注和编辑:
Enumerable.Range(0, 1).Select(__ => GenerateLongString())
最后的完整代码更改为
Enumerable.Range(0, 1).Select(__ => GenerateLongString()).ToArray().AsEnumerable()
,
然后初始化需要几秒钟,然后CPU用于100%(没有问题发生)GenerateLongString()
在GC上减轻重量并且在CPU上更加密集时,则CPU变为100%。因此原因与此方法的实现有关。但是,有趣的是,如果在没有IEnumerable的情况下调用当前形式的GenerateLongString()
,CPU也会达到100%:
Parallel.For(0, int.MaxValue, _ => GenerateLongString());
所以GenerateLongString()
的沉重不是这里唯一的问题。clr.dll!WKS::gc_heap::wait_for_gc_done
,
等待GC完成。这发生在string.Concat()
的GenerateLongString()
内。完整代码:
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"));
}
}
您的线程在clr.dll!WKS::gc_heap::wait_for_gc_done
上被阻止的事实表明垃圾收集器是您的应用程序的瓶颈。尽可能地,您应该尝试限制程序中的堆分配数量,以减少对gc的压力。
也就是说,还有另一种加快速度的方法。默认情况下,在桌面上,GC配置为使用计算机上的有限资源(以避免减慢其他应用程序的速度)。如果你想充分利用可用的资源,那么你可以activate server GC。此模式假定您的应用程序是计算机上运行的最重要的事情。它将提供显着的性能提升,但使用更多的CPU和内存。