2

我们的多线程应用程序执行了一个冗长的计算循环。平均而言,完成一个完整的周期大约需要 29 秒。在此期间, GC 中的 .NET 性能计数器% time测量为 8.5%。它全部由 Gen 2 系列制成。

为了提高性能,我们为我们的大对象实现了一个池。我们实现了 100% 的重用率。整个周期现在平均只需 20 秒。“% time in GC”显示在 0.3...0.5% 之间。现在 GC 只进行 Gen 0 收集。

让我们假设,池化被有效地实现并且忽略了执行所需的额外时间。比我们获得了大约 33% 的性能提升。这与之前 8.5% 的 GC 值有何关系?

我有一些假设,希望能得到确认、调整和修正:

1)“GC中的时间”(如果我没看错的话)确实测量了2个时间跨度的关系:

  • 2个GC周期之间的时间和
  • 上次完整 GC 循环所用的时间,该值包含在第一个跨度中。

第二个时间跨度不包括的是为阻塞 GC 停止和重新启动工作线程的开销。但这怎么可能占总执行时间的 20%?

2)频繁阻塞GC线程可能会引入线程之间的争用?这只是一个想法。我无法通过 VS 并发分析器确认这一点。

3)与此相反,可以确认非池化应用程序的页面未命中数(性能计数器:内存 -> 页面错误/秒)显着高于具有低 GC 率的应用程序(每秒 25.000 次) (每秒 200 个)。我可以想象,这也会带来很大的进步。但是什么可以解释这种行为呢?是不是因为频繁的分配导致使用虚拟内存地址空间中更大的区域,因此更难保留到物理内存中?又如何衡量以确认是这里的原因呢?

顺便说一句:GCSettings.IsServerGC = false,.NET 4.0,64 位,在 Win7、4GB、Intel i5 上运行。(很抱歉这个大问题.. ;)

4

2 回答 2

3

然后我们的性能提高了大约 33%。这与之前 8.5% 的 GC 值有何关系?

通过合并,您还可以节省花费在 中的时间new,这可能是相当可观的,但我不会花时间尝试平衡数字。

与其“在嘴里看礼物马”,为什么不继续寻找其他“瓶颈”呢?

当你删除一个性能问题时,你会让其他人占用更大比例的时间,因为分母更小。因此,只要您知道如何查找它们,就更容易找到它们。

这是一个例子,一个方法。 你解决了一个大问题。这使得下一个更大,按百分比,所以你清理了那个。冲洗并重复。它可能会花费很少的时间,以至于您需要在它周围包裹一个临时的外循环,只是为了让它花费足够长的时间来调查。你继续这样下去,逐渐使程序花费的时间越来越少,直到你达到收益递减。

这就是如何使代码快速。

于 2011-04-10T21:42:14.857 回答
2

预先分配对象提高了并发性,线程不再需要进入保护垃圾收集堆的全局锁来分配对象。锁的持有时间很短,但显然您分配了很多对象,因此线程争夺锁的可能性不大。

'time in GC' 性能计数器测量用于收集而不是执行常规代码的 cpu 时间百分比。如果有很多 gen#2 集合,并且您分配对象的速度如此之快,以至于后台集合无法再跟上并且必须阻止线程,您将获得一个很大的数字。拥有更多线程会使情况变得更糟,您可以分配更多。

于 2011-04-10T17:59:37.710 回答