1

我有一个相当复杂的 F# 应用程序,我使用 F# 脚本使用#time指令检查其性能。这个想法是我在更改代码后运行脚本以跟踪性能并避免引入速度或内存问题。

我得到的报告是这样的:

Real: 00:00:02.908, CPU: 00:00:02.948, GC gen0: 237, gen1: 3, gen2: 1

但是在最后一次更改之后,我得到了这个:

Real: 00:00:03.058, CPU: 00:00:03.057, GC gen0: 262, gen1: 262, gen2: 0

我很难弄清楚发生了什么。

更多的第 1 代垃圾收集意味着(?)有更多的长寿对象在第 0 代的垃圾收集中幸存下来 - 但为什么突然从 3 跳到 262?为什么第 0 代和第 1 代的垃圾回收次数相同?如果由于某种原因在代码更改后有更多长寿命的对象,我会期待这样的事情:

Real: 00:00:02.908, CPU: 00:00:02.948, GC gen0: 212, gen1: 54, gen2: 1

即垃圾收集对象的总数相似,但第一代更多。

还是我完全误解了这些数字(我在文档中找不到描述)?

更新

正如评论中所指出的,我完全误解了这些数字:它们是每一代执行的垃圾收集的数量,而不是对象的数量。

说了这么多,我更纳闷了:好像我的应用程序把内存置于这样一种状态,每次0代垃圾回收都会触发1代垃圾回收,我无法想象这种状态会是什么。我尝试了各种测试程序来复制这种行为,但没有运气。

我仍然必须按照评论和垫的答案中的建议尝试分析器。

更新 2

我按照评论中的建议尝试了 CLR 探查器 - 为此,我将 F# 脚本转换为程序并从探查器运行它。

我得到的报告与垃圾收集完全不同#time

  • 第 0 代:279
  • 第 1 代:5
  • 第 2 代:2。

神秘 - 与独立程序相比,使用 FSI.exe 的副作用?错误#time

4

1 回答 1

2

.NET GC 是三代 GC。正如 John Palmer 所指出的,最后三个数字是这些世代运行的集合数量。较大的数字通常表示性能不佳gen1gen2因为 GC 必须在每个垃圾收集过程中一次又一次地标记那些未使用的对象。有关 .NET GC 的更深入介绍,请查看这篇不错的文章

这里有一些建议:

  1. 确保在两次测量之间“重置会话”,以便第一个会话的未使用对象不会影响第二个会话。
  2. 找到三个更大的输入来测量。尽管这两个报告中的执行时间相似,但我希望第一种方法更具可扩展性。如果您有多个输入大小来测试可伸缩性,那就太好了。
  3. 测量几次以记录模式,因为您可能只是观察到异常会话。
  4. 如果有疑问,请在 Visual Studio 中进行全面分析。他们的报告在性能预测方面更加全面和可靠。
于 2012-09-17T05:26:47.643 回答