我正在创建一个桌面应用程序,它具有可能运行几秒钟的计算量大的操作。显然,需要最小化该操作的时间。该操作相当容易并行(单个子任务),每个子任务在单个线程上大约需要50ms。在多个线程上,每个子任务需要4-5倍的时间,因为在GC中花费了40-50%的时间,从而有效地完全取消了加速。
所以我需要减少GC的工作量。我的第一个想法是试图找到哪种类型的对象被垃圾收集最多,但我意识到虽然我经常进行内存分析,但我从未搜索过这样的模式。通常查看堆快照或堆快照之间的差异,但这些显示活动的对象,而不是在这些快照之间创建和处置的对象。所以这是我的第一个问题:什么是最简单的方法来查找创建哪些类型和垃圾收集最多?我试图寻找方法调用计数来查看是否经常怀疑某些构造函数,但是以百万计数创建的所有对象都只是小结构类型。如果我理解正确的话,这些对GC应该没有影响吗?
该算法可创建数十万个单独的结果点对象。这些当然不应该是gc'd,因为它们代表了操作的输出。但它引出了我的第二个问题:在GC中花费的时间主要取决于对象的总数,还是主要取决于实际收集的对象数量?我是否应该尝试限制结果对象的数量,而是使用更少但更大的结果对象?
编辑:我通过使用VS 2010并发可视化工具找到了在GC中花费的时间。此外,在并行代码段中,阻塞线程的大多数部分都在等待gc
编辑:我应该澄清性能问题是因为执行在工作站GC上有效地序列化了。例如,请参阅本文中描述的性能问题。
我无法阻止垃圾收集器阻塞我的线程(我认为我不希望服务器GC用于桌面应用程序,对吗?)。因此,为了获得此操作的线性加速,我需要减少调用GC的次数。浪费的大部分时间实际上被其他线程阻塞,等待一个线程执行GC。
答案 0 :(得分:4)
就个人而言,如果你的任务只需要50毫秒来执行,那么线程创建的开销等将比你的实际工作花费更多的时间,这就是你所看到的。所以你可能无法进入太远。
至于看看那里有什么,我使用过的最好的工具是ANTS Profiler(内存和性能)。从那里你可以看到记忆中的物体,时间点之间的差异以及“你应该得到你想要的”“执行次数”。
答案 1 :(得分:1)
也许您应该考虑增加对象之间的缓存命中率。
因此,您不是创建新的结构点,然后在列表/枚举中执行计算,而是尝试分配固定的点数组,然后不断重用这些点。这样,您只需分配一次对象,执行计算然后返回。您将受益于热缓存,如果您能够完全重用该阵列,您将不会遇到任何GC。
答案 2 :(得分:1)
老问题,但对那些偶然发现的人来说......
我遇到了完全相同的问题并通过设置服务器模式垃圾回收http://msdn.microsoft.com/en-us/library/ms229357(v=vs.110).aspx来永久修复它。
在app.config
添加:
<runtime>
<gcServer enabled="true" />
</runtime>
这已经使我的代码加速了一个数量级,没有我能找到的副作用。
如果你确切地知道你在哪里产生了很多GC,我还发现LowLatency http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode(v=vs.110).aspx将我的GC带到了单代的GC:
GC.Collect ' pre-emptively collect before time-critical region
Dim oldmode As GCLatencyMode = GCSettings.LatencyMode
RuntimeHelpers.PrepareConstrainedRegions()
Try
GCSettings.LatencyMode = GCLatencyMode.LowLatency
' Work that allocates tons of memory here
Finally
GCSettings.LatencyMode = oldmode
End Try
(PrepareConstrainedRegions希望确保始终执行Finally块,但我不完全确定这是正确的。)
答案 3 :(得分:0)
这些结果点对象。如在标准struct Point中? 不能从这里说,但你试过为他们预先分配空间。你的大部分GC调用都可以为他们分配内存,这需要付出很多努力,在较大的块中进行操作,或者甚至在一次性操作中如果可以计算出的数量应该会给你带来提升。
另一种选择可能是不安全的代码,因为您可以在工作站上获得该权限。 不知道你有没有提出你的观点,但可能只是分配一块内存然后用指针运算来破解它。