gprof不使用该功能进行计时、输入orexit,但用于对函数 A 调用任意函数 B 进行调用计数。
相反,它使用通过计算每个例程中的 PC 样本而收集的自用时间,然后使用函数到函数的调用计数来估计应向调用者收取多少自用时间。
例如,如果 A 调用 C 10 次,B 调用 C 20 次,并且 C 有 1000ms 的自时间(即 100 个 PC 样本),则gprof知道C被调用了30次,其中33个样本可以记入A,而另外67个样本可以记入B。
类似地,样本计数沿着调用层次结构向上传播。
所以你看,它不计时函数的进入和退出。
它得到的测量结果非常粗略,因为它不区分短调用和长调用。
另外,如果 PC 样本发生在 I/O 期间或未使用 -pg 编译的库例程中,则根本不计算在内。
而且,正如您所指出的,在存在递归的情况下它非常脆弱,并且可能会给短函数带来显着的开销。
另一种方法是堆栈采样,而不是 PC 采样。
当然,捕获堆栈样本比捕获 PC 样本更昂贵,但需要的样本更少。
例如,如果一个函数、一行代码或您想要进行的任何描述在 N 个样本总数中的分数 F 上是明显的,那么您就知道它花费的时间分数是 F,具有标准差的 sqrt(NF(1-F))。
因此,举例来说,如果您采集 100 个样本,其中 50 个样本上出现一行代码,那么您可以估计该行代码占 50% 时间的成本,不确定性为 sqrt(100*.5*.5) = +/- 5 个样本或介于 45% 和 55% 之间。
如果您采集 100 倍的样本,则可以将不确定性降低 10 倍。
(递归并不重要。如果一个函数或一行代码在单个样本中出现 3 次,则算作 1 个样本,而不是 3 个样本。
函数调用是否短也没关系——如果它们被调用的次数足够多而花费了很大一部分,它们就会被捕获。)
请注意,当您寻找可以修复的问题以提高速度时,确切的百分比并不重要。
重要的是找到它。
(其实你只需要看到一个问题twice要知道它足够大,可以修复。)
That's 这项技术 https://stackoverflow.com/a/378024/23771.
附:不要陷入调用图、热路径或热点中。
这是一个典型的调用图老鼠巢。黄色是热点路径,红色是热点。
这表明,在这些地方都不存在一个多汁的加速机会是多么容易:
最有价值的东西是十几个随机的原始堆栈样本,并将它们与源代码相关联。
(这意味着绕过分析器的后端。)
添加:只是为了说明我的意思,我从上面的调用图中模拟了十个堆栈样本,这就是我发现的
- 3/10 样品正在调用
class_exists
,一个用于获取类名,两个用于设置本地配置。class_exists
calls autoload
哪个调用requireFile
,其中两个调用adminpanel
。如果可以更直接地做到这一点,可以节省约 30%。
- 2/10 样品正在调用
determineId
,这称为fetch_the_id
哪个调用getPageAndRootlineWithDomain
,这又调用了三个级别,终止于sql_fetch_assoc
。获取 ID 看起来很麻烦,而且花费了大约 20% 的时间,而且这还不包括 I/O。
因此,堆栈示例不仅告诉您一个函数或一行代码花费了多少包含时间,还告诉您为什么要这样做,以及完成它可能需要哪些愚蠢的事情。
我经常看到这种现象——奔腾的普遍性——用锤子打苍蝇,不是故意的,只是遵循良好的模块化设计。
ADDED: Another thing not to get sucked into is flame graphs.
For example, here is a flame graph (rotated right 90 degrees) of the ten simulated stack samples from the call graph above. The routines are all numbered, rather than named, but each routine has its own color.
Notice the problem we identified above, with class_exists (routine 219) being on 30% of the samples, is not at all obvious by looking at the flame graph.
More samples and different colors would make the graph look more "flame-like", but does not expose routines which take a lot of time by being called many times from different places.
Here's the same data sorted by function rather than by time.
That helps a little, but doesn't aggregate similarities called from different places:
Once again, the goal is to find the problems that are hiding from you.
Anyone can find the easy stuff, but the problems that are hiding are the ones that make all the difference.
ADDED: Another kind of eye-candy is this one:
where the black-outlined routines could all be the same, just called from different places.
The diagram doesn't aggregate them for you.
If a routine has high inclusive percent by being called a large number of times from different places, it will not be exposed.