在分析(采样)应用程序时忽略 GC
Posted
技术标签:
【中文标题】在分析(采样)应用程序时忽略 GC【英文标题】:Ignoring GC while profiling (sampling) an application 【发布时间】:2014-02-17 12:20:20 【问题描述】:我正在使用 VS 2012 中的采样分析应用程序(尽管分析器并不重要)。我对性能瓶颈所在有一个很好的引导,但是,我受到以下事实的阻碍,即正在进行大量内存分配,并且垃圾收集器似乎严重扭曲了我的分析(我可以在一定程度上看到 GC 效果在 CLR Profiler 和并发可视化工具中)。
有没有办法以某种方式摆脱 GC 运行时采集的样本?我可以使用其中任何一个:
在 GC 运行时忽略收集的样本(按函数指针过滤?) GCing 花费的时间和实际工作花费的时间分开 增加 GC 限制以有效地“关闭”以进行分析 实际关闭GC问题是我几乎不知道我需要优化什么。通过减少分配等来优化 GC 的尝试对没有附加调试器的发布版本的实际影响非常低,所以我真的想知道有多少分析结果是由于禁用优化等,以及有多少代码可以有待改进(我们的大部分项目都使用了相关代码,因此即使性能提高 10% 也会产生巨大影响)。
【问题讨论】:
您无法将其关闭,也无法希望它消失。在我看来,您发现了应该首先解决的真正的瓶颈。你产生了太多的垃圾。也许您可以重复使用缓冲区。也许这太不切实际了,而且它已经达到了预期的效果。 @HansPassant 问题是我不知道代码是否真的只是 CPU 密集型的,因为获得最多样本的特定函数也在进行大量分配(以及其余的由于以前的优化,应用程序几乎没有任何作用),因此导致该代码中的 GC,即使这不一定是它们的原因。我无法将 GC 的“真实”工作与“偷走”的 CPU 隔离开来——即。问题是 - 优化哪个:分配还是计算?如何使用分析数据将两者分开? 好吧,只是不要假设 GC 不是真正的工作。也就是说,分配内存不是免费的,而且你的代码在用户机器上也会被它减慢。所以问题不在于你的代码太密集,问题在于它使用了太多的内存。这是完全正常的,相信分析器告诉你的。并专注于通过更有效地使用内存来改进它。 @HansPassant 好吧,这正是我的问题——我不知道是不是 GC 给我带来了麻烦。我的代码绝对是 CPU 密集型的,而且肯定会造成很大的内存压力。但我不知道如何使用分析数据来确定两者中哪一个更重要。在实践中,我发现虽然我的内存优化在 CLR 分析器中创造了奇迹(分配的字节从 2.5 GiB 下降到 ~100 MiB),但在发布版本中,它们对性能的影响非常低。似乎进一步优化内存使用也将收效甚微。 我会说它比这更复杂。 GC 可以做很多工作而不影响应用程序的性能。 GC 的最大影响来自于它有时必须挂起托管线程以安全地在堆上移动对象。发生这种情况时,CLR 会输出 ETW 事件。将这些事件与您自己的事件相关联可以让您很好地了解 GC 如何影响您的应用程序的性能。 【参考方案1】:我建议你退后并try a different approach。 以下是它的工作原理:
您的程序中存在速度错误。 (不太可能没有。) 如果您找到并修复它,您将节省一些时间。 假设是 50%。
这意味着,如果您只是在 IDE 下运行它,并在等待它时手动暂停它,那么您有 50% 的机会会在您节省的时间内停止它。 通过查看调用堆栈、调用堆栈上的每一行代码以及可能的数据,找出它在做什么以及为什么这样做。
根据您看到的情况,执行少量次数,例如 5、10 或 20 次。 您将看到它在大约 50% 的样本上执行速度错误,保证。
这会告诉你一些探查器不会告诉你的事情,例如:
1234563为什么。采样分析器可以为您提供行级包含时间,但它无法告诉您花费时间的原因,并且在不知道原因的情况下您不能确定您不需要它。 OTOH,如果样本落在 GC 中,请忽略它并寻找new
,因为 new
也很昂贵,它是导致 GC 的原因。
1234563绕开它。采样分析器不会告诉您这一点,因为它是一个“CPU 分析器”,这意味着只要您的程序被阻塞,它就会休眠。如果您切换到仪表化分析器,它不会为您提供线级精度。它也不会告诉你花费时间的原因。
如果你尝试这样做,你可能不得不忍受一些嘲笑,但它会得到你想要的结果。 更重要的是,如果您发现并修复了 50% 速度的错误,程序将快 2 倍。 这具有使进一步的速度错误更容易找到的效果。 例如,如果最初除了 50% 的错误之外还有 25% 的速度错误,现在它是 50% 的错误,如果您找到并修复它它,您将快 4 倍。 你会惊讶地发现,你可以一直这样下去,直到你不能再这样做了,然后你就会接近最佳状态。
【讨论】:
我很喜欢你的回答。我知道这不是等待,因为 CPU 实际上是全速运行,但这些都是很好的建议。我将尝试盲停,看看它是否会产生一些有趣的东西。到目前为止,似乎 zip 解压缩是麻烦制造者,这可以帮助确定确切的原因(如果我能做点什么)。 @Luaan:实际上我更喜欢“机会”这个词而不是“错误”,但它的音节要多得多。它传达了这样一种观念,即程序可能很好,但仍然可以加快速度。 OTOH,你发现它的方式是 - 好像它是一个错误。 好的,尽管调试器中发生了大量变化,我仍然能够找到删除几乎 70% 运行时的方法。正如我所怀疑的,它实际上是在 ZIP 库中(纯 C#,但非常慢)。解决一些不必要的越界检查(是的,anything & 0xFF
的值将不会超出 256 项数组的范围)和其他一些事情(比如遍历一个字节一个字节一个字节的数组,而不是使用 Array.Copy
/ Buffer.Copy
) 有很大帮助。 GC 似乎只是运行多线程时的一个重要问题,但我在生产中不需要它。
@Luaan:太好了!不知道比猜测更好吗?祝你好运。
哦,你知道,work
和 fun
之间的微妙平衡:D以上是关于在分析(采样)应用程序时忽略 GC的主要内容,如果未能解决你的问题,请参考以下文章
当我对一个程序进行采样并且它实际上比不进行分析时运行得更快时,为啥会这样?
突然弹出王者荣耀停止运行,GC超时导致的后台应用崩溃问题分析