C#性能分析器是如何工作的?

13

我很好奇典型的C#性能分析器是如何工作的?

虚拟机中是否有特殊的钩子?

是否很容易扫描字节码以查找函数调用并注入调用以启动/停止计时器?

还是说这很困难,所以人们付费购买此类工具?

(顺便提一句,我发现这很有趣,因为它非常罕见——谷歌完全错过了搜索“C#性能分析器是如何工作的?”,结果全都是关于空调的......)


2
本文有很多好的信息:http://msdn.microsoft.com/en-us/magazine/cc301725.aspx。还有http://msdn.microsoft.com/en-us/library/bb384547.aspx。 - Jim Mischel
通常,许多分析器会频繁地对堆栈进行快照,以查看其当前状态。然后,他们使用这些快照构建统计信息。当然,C#可能不是这种情况,所以请谨慎对待。 - Chris Eberle
我太懒了,不想读这个并将其转换为一个适当的答案,但是http://msdn.microsoft.com/en-us/library/ms404386.aspx对你应该有用。 - Michael Greene
2
请注意,这不是C#分析器 - 它是CLR分析器。因此:http://www.google.com/search?q=how+does+a+CLR+oprofiler+work%3F - John Saunders
“Typical”的定义是什么?“C#”又是什么意思?你是指具体的C#,还是指CLR? - Ira Baxter
显示剩余3条评论
4个回答

3

3
知道这件事很好,但与问题的答案无关。 - Dan J
1
+1 给反对者的计数器。你为 OP 的问题提供了直接的答案。 - Ira Baxter
CLR Profiler附带源代码,如果有人对它的工作原理感兴趣。 - Omer Raviv
@Omer Raviv:这正是我想说的 :) - GregC

3
很难扫描字节码并注入控制语句来启动/停止计时器,因此需要专门的工具。
不仅难度大,而且这是一种非常间接的查找瓶颈的方法。瓶颈通常指代码中一个或一小部分语句占用了大量时间,可以通过优化来显著减少 - 即它不是真正必要的,即它是浪费的。如果您可以计算出一个例程的平均包含时间(包括IO时间),并将其乘以调用次数后除以总时间,则可以确定该例程所占的时间百分比。如果这个百分比很小(比如10%),那么您在其他方面可能会遇到更大的问题。如果百分比较高(比如20%到99%),则该例程内部可能存在瓶颈。因此,现在您需要在例程内部进行寻找,并查看其调用的内容以及他们所花费的时间。此外,还需要避免递归造成的调用图混乱。
有一些性能分析工具(例如Zoom适用于Linux、Shark和其他工具),它们采用了不同的原则。 这个原则是存在一个函数调用堆栈,在所有时间内,一个例程负责(无论是执行任务还是等待其他例程完成它请求的任务)时,它都在堆栈上。 因此,如果它负责50%的时间(比如说),那么它就在堆栈上占用50%的时间,而不管它被调用多少次或每次需要多长时间。 不仅例程在堆栈上,而且消耗时间的具体代码行也在堆栈上。 你不需要寻找它们。 另外,你不需要精确测量。 如果你取10,000个堆栈样本,那么有问题的代码行将被测量为50 +/- 0.5%。 如果你取100个样本,它们将被测量为50 +/- 5%。 如果你取10个样本,它们将被测量为50 +/- 16%。 在每种情况下,你都可以找到它们,这是你的目标。 (递归并不重要。这只意味着给定的行可能会在给定的堆栈样本中出现多次。)
在这个主题上,存在很多困惑。无论如何,最有效的用于查找瓶颈的性能分析工具是那些基于墙钟时间对堆栈进行采样,并按行报告百分比的工具。(如果将某些有关性能分析的误解置于透彻的背景下,则很容易理解这一点。)

+1!听起来我们应该为dotNET编写一个新的分析器,并以这个简单的概念为基础。至于用户界面,我想到了WinDirStat,只是我们会显示在各种方法中花费的时间,按名称空间.类别进行分组。 - GregC
@GregC:我很久以前写过一个类似的东西。当你按下两个Shift键时,它会收集堆栈样本。然后我有一个蝴蝶视图聚焦于一行代码,而不是一个函数。它从高百分比的行开始,并且您可以向上或向下移动到相邻的行。这是一个很好的演示,但我发现对于严肃的工作,由于许多原因,它无法击败纯手动方法,所以我让它休息了。无论如何,如果您做类似于Zoom的事情,我认为您会成为赢家。 - Mike Dunlavey
@GregC:我所说的不如手动方法是指,手动收集几个样本、选择一个“热门”线路并从该线路向下和向上查看树形结构确实更麻烦。这有点繁琐。但如果某些东西引起了我的注意,我会取更多的样本直到它再次出现,然后我会研究它以叙述程序在做什么以及为什么要这样做。这可能意味着查看除堆栈之外的其他数据。当我完全理解它时,我可以判断它是否真的浪费并且可以用更好的东西替换。因此,找到高百分比的线路只是一个引导过程。 - Mike Dunlavey

1

1) 没有所谓的“典型”。人们通过多种方式收集配置文件信息:对PC进行时间抽样,检查堆栈跟踪,捕获方法/语句/编译指令的执行次数,插入探针以收集计数,并可选择调用上下文来获取调用上下文基础上的配置文件数据。每种技术可能会以不同的方式实现。

2) 有性能分析“C#”和性能分析“CLR”。在微软的世界中,你可以对CLR进行性能分析,并将CLR指令位置反向转换为C#代码。我不知道Mono是否使用相同的CLR指令集;如果他们没有,那么你就不能使用微软的CLR分析器;你必须使用Mono的IL分析器。或者,你可以对C#源代码进行仪表化处理以收集性能分析数据,然后在MS、Mono或某个C#兼容的自定义编译器上编译/运行/收集该数据,又或者是在嵌入式系统(如WinCE)中运行的C#,其中空间宝贵而CLR内置功能往往会被省略。

一种仪器化源代码的方法是使用源到源的转换,将代码从其初始状态映射到包含收集数据代码以及原始程序的代码。这篇关于如何使用程序转换系统插入测试覆盖探针的论文展示了当执行代码块时插入设置块特定布尔标志语句的方式。计数分析器用计数增量指令替换了这些探针。时间分析器为这些探针插入了时钟快照/增量计算。我们的C#分析器实现了C#源代码的计数和时间分析两种方式;它还通过使用收集执行路径的更复杂的探针来收集调用图数据。因此,它可以以这种方式在调用图上生成定时数据。这种方案适用于任何你能得到半可靠分辨率时间值的地方。

1

它所说的关于采样的内容只是 gprof 模型,已经有近 30 年的历史了。现代采样器做得更好。 - Mike Dunlavey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接