我有一个有很多独立计算的程序,所以我决定把它并行化。
我使用Par等位基因。
结果表明,在大多数情况下,双核机- CPU利用率约为80%-90%。然而,使用双Xeon机器(即8核),我的CPU利用率仅为30%-40%,尽管程序在并行部分上花费了相当长的时间(有时超过10秒),而且我看到与串行部分相比,它在这些部分中使用了大约20-30个线程。每个线程需要超过1秒才能完成,因此我认为它们没有理由不并行工作--除非存在同步问题。
我使用了内置的VS2010分析器,结果很奇怪。即使我只在一个地方使用锁,分析器报告说,大约85%的程序时间用于同步(也是5-7%的睡眠,5-7%的执行,低于1% IO)。
锁定的代码只是缓存(字典) get/add:
bool esn_found;
lock (lock_load_esn)
esn_found = cache.TryGetValue(st, out esn);
if(!esn_found)
{
esn = pData.esa_inv_idx.esa[term_idx];
esn.populate(pData.esa_inv_idx.datafile);
lock (lock_load_esn)
{
if (!cache.ContainsKey(st))
cache.Add(st, esn);
}
}
lock_load_esn
是类型对象类的静态成员。
esn.populate
对每个线程使用单独的StreamReader从文件中读取。
但是,当我按下同步按钮查看是什么导致了最大的延迟时,我看到分析器报告的行是函数入口行,而不是报告锁定的部分本身。
它甚至没有报告包含上述代码的函数(提醒-程序中的唯一锁)作为阻塞配置文件的一部分,噪音级别为2%。在噪音水平为0%的情况下,它报告了程序的所有功能,但我不明白为什么它们被计算为阻塞同步。
所以我的问题是-这是怎么回事?
为什么85%的时间花在同步上?
如何找出我的程序的平行部分的真正问题是什么?
谢谢。
Update:在深入到线程中(使用非常有用的可视化工具)之后,我发现大部分同步时间都花在等待GC线程完成内存分配上,并且由于通用数据结构调整了大小操作,所以需要频繁的分配。
我必须了解如何初始化数据结构,以便它们在初始化时分配足够的内存,从而可能避免GC线程的争用。
今天晚些时候我会报告结果。
更新:内存分配似乎确实是造成问题的原因。当我对并行执行类中的所有字典和列表使用初始容量时,同步问题就更小了。我现在只有大约80%的同步时间,CPU利用率达到70% (以前的峰值只有40%)。
我对每个线程进行了更深入的钻研,发现现在对GC分配的许多调用都用于分配小对象,而这些对象不是大型字典的一部分。
我通过为每个线程提供一个预先分配的对象池来解决这个问题,而不是调用“新”函数。
因此,我基本上为每个线程实现了一个单独的内存池,但是以一种非常粗糙的方式,这是非常耗时的,而且实际上也不是很好--我仍然需要使用许多新的方法来初始化这些对象,直到现在我才在全局范围内执行了一次,而且GC线程上的争用减少了,即使必须增加池的大小。
但这绝对不是我喜欢的解决方案,因为它不容易泛化,我也不想写我自己的内存管理器。
是否有一种方法可以告诉.NET为每个线程分配预定义的内存量,然后从本地池获取所有内存分配?
发布于 2010-05-25 16:20:08
你能少分配一点吗?
我有过几次类似的经历,看看糟糕的特权,发现问题的核心是GC。然而,在每一种情况下,我都发现自己在某个内部循环中意外地失掉了记忆,分配了大量不必要的临时对象。我会仔细查看代码,看看是否存在可以删除的分配。我认为程序“需要”在内部循环中大量分配是很少见的。
https://stackoverflow.com/questions/2902984
复制相似问题