我在考虑使用反射来生成探查器。让我们说我正在生成没有问题的代码;如何正确衡量或组织结果?我主要关注CPU时间,但欢迎内存建议
答案 0 :(得分:1)
有很多bad ways to write profilers。
我写了20多年前我认为非常好的东西。 也就是说,它做了一个不错的演示,但是当它归结为严肃的性能调整时,我得出结论,确实没有什么比愚蠢的旧手动方法and here's why更好,并且提供更好的结果。
无论如何,如果你正在写一个分析器,我认为它应该做什么:
它应该在不可预测的时间对堆栈进行采样,并且每个堆栈样本应该包含正在调整的代码中的行号信息,而不仅仅是函数。在系统功能中进行编辑并不是那么重要。
它应该能够在阻塞时间内进行采样,如I / O,睡眠和锁定,因为这些可能会导致CPU操作缓慢。
它应该有一个用户可以使用的热键,以便在他们真正关心的时间内进行采样(比如在等待用户做某事时)。
不假设有必要获得测量精度,需要大量频繁样本。这是非常基本的,它是共同智慧的重大转变。原因很简单 - 如果您支付的价格未能找到,那么衡量问题并没有任何好处。
这就是剖析器发生的事情 - 加速隐藏,因此用户满足于找到一两个小的加速,而巨人则逃脱。
巨大的加速是占用大部分时间的加速,并且找到它们所需的堆栈样本数量与它们所花费的时间成反比。如果程序花费30%的时间做一些可以避免的事情,那么在它被看到两次之前需要(平均)2 / 0.3 = 6.67个样本,这足以确定它。
要回答您的问题,如果样本数量很少,那么存储它们的方式并不重要。如果你愿意,可以将它们打印到文件中。
它不一定要快,因为你在保存样品时没有取样。
做什么允许找到这些加速比是用户实际查看和理解单个样本的时间。分析器具有各种UI - 热点,呼叫计数,热路径,呼叫图,呼叫树,火焰图,虚假3位数"统计",等等,等等。 即使它做得好,那只是计时信息。 它并没有告诉你为什么花费的时间,以及你需要知道的事情。 如果需要,可以制作眼睛糖果,但让用户看到实际的样品。
......祝你好运。
增加:示例如下所示:
main:27, myFunc:16, otherFunc:9, ..., someFunc;132
这意味着main
位于第27行,呼叫myFunc
。 myFunc
位于第16行,正在调用otherFunc
,依此类推。最后,它在第{132}的someFunc
中,没有打电话给任何人(或打电话给你无法识别的东西)。
不需要行范围。
(如果你很想担心递归 - 不要。如果同一个函数在一个样本中出现不止一次,那就是递归。它不会影响任何事情。)< / p>
您不需要大量样本。 当我这样做时,抽样根本不是自动的。 我只是让用户同时按下两个shift键,这将触发一个样本。 因此,用户将抓住10或20个样本,但是在程序执行阶段用户采取样本以使其慢慢地惹恼用户是至关重要的, 比如在点击某个按钮的时间和UI响应的时间之间。 另一种方法是使用热键在按下时在计时器上运行采样。 如果程序只是一个没有用户输入的命令行应用程序,它只能在执行时一直采样。 采样频率不一定非常快。 目标是在程序阶段获得适度数量的样本,主观上是缓慢的。 如果您需要查看太多样本,那么当您查看它们时,您需要随机选择一些样本。
检查示例时要做的是查看示例中的每行代码,这样您就可以完全理解为什么程序在那个时刻花费了。 如果它正在做一些可能避免的事情, 如果你在另一个样本上看到类似的东西,你就发现了加速。 加速多少钱?这么多(the math is here):
例如,如果您查看三个样本,并且其中两个样本您看到可避免的代码,修复它将为您提供加速 - 可能更少,可能更多,但平均 4x。 (这就是巨型加速的意思。你得到它的方法是研究个别样本,而不是通过测量任何东西。)
有视频here。