2

我想使用 callgrind 来分析我的程序,但是速度太慢了。我想要做的是使用 kcachegrind 生成一个调用图,其中每个节点显示程序在哪个函数中花费的百分比。你能告诉我我可以安全地禁用哪些功能以获得更好的性能,以便仍然生成此信息吗?

非常感谢!

4

1 回答 1

5

快速概览

Callgrind 本质上是一个缓存分析器(指令和数据),它以函数级粒度工作,以重现调用图。分析器观察程序执行期间触发事件的动作,并更新模拟器维护的各种聚合计数器。

然而,这种对缓存事件的细粒度模拟是以程序运行时间为代价的。您应该知道,即使关闭了所有分析并且没有收集到有用的数据,Callgrind在运行时仍将至少有大约 2-4 倍的命中率。主动收集数据时,平均速度会慢 10-20 倍

这个理论上的最小值是否可以满足您的要求?如果没有,您应该考虑其他分析选项 - 在此处讨论。但是,如果通过一些仔细的控制,将程序的大而无趣的块加速到只有 2-4 倍的减速听起来是合理的,请继续阅读!

可用的钩子

Callgrind 提供 2 种形式的分析数据收集控制。为了做出明智的选择,了解它们的相互依赖关系很重要:

  1. 仪表状态- 禁用时,不会观察到任何程序操作,因此不会触发或收集任何事件。模拟器基本上切换到“空闲”状态;这可以帮助您达到我上面提到的理论上的 2-4 倍最小值(请参阅Nulgrind)。

    但请注意,这应该小心使用!虽然它提供了有吸引力的好处,但这会对准确性产生重大影响。从文档

    然而,这只应谨慎使用,并且以粗略的方式使用:每次模式更改都会重置模拟器状态(即内存块是否被缓存)并刷新 Valgrinds 检测代码块的内部缓存,从而导致切换时的延迟损失时间。

  2. 集合状态- 禁用时,聚合计数器不会更新触发事件。这提供了一种将收集的数据简化到调用堆栈中感兴趣的部分的方法。

    然而,直观地说,这并没有显着加快执行时间。当然,需要打开仪器才能启用收集。

命令

valgrind --tool=callgrind   
    --instr-atstart=<yes|no>     ;; default = yes 
    --collect-atstart=<yes|no>   ;; default = yes
    --toggle-collect=<function>  ;; Toggle collection at entry/exit of specific function
<PROGRAM> <PROGRAM_OPTIONS>

Instrumentation - 一开始关闭它表示您必须在适当的时间再次打开它。2 种替代方法:

  1. 在程序执行期间,在适当的时候从 shell 中使用以下命令。

    callgrind_control -i <on|off>
    

    这将需要对程序执行的可见性以及由于部署命令的延迟而对准确性有一定的容忍度。当然,您可以使用一些 shell 技巧来提供帮助。

  2. 将以下宏插入您的程序代码并重新编译您的二进制文件。

    CALLGRIND_START_INSTRUMENTATION;
    CALLGRIND_STOP_INSTRUMENTATION;
    

集合- 同样,如果在开始时禁用,则需要围绕代码的有趣部分切换集合。2 种替代方法:

  1. 在启动期间使用--toggle-collect=<function>标志。根据定义,这将包括此函数中的所有子调用。如果您因此可以将特定父函数识别为瓶颈,那么这可能是隔离相关数据并保持生成的调用图最小的有用方法。

    提示:函数名支持通配符!

  2. 在程序代码的相关部分之前和之后使用以下宏并重新编译您的二进制文件。这可以在函数中为您提供更细粒度的控制。

    CALLGRIND_TOGGLE_COLLECT;
    

概括

结合上述所有想法,一个好的方法是:

#include <callgrind.h>

// Uninteresting program chunk

CALLGRIND_START_INSTRUMENTATION;

// A few extra lines to allow cache warm-up

CALLGRIND_TOGGLE_COLLECT;
// Portion to profile
CALLGRIND_TOGGLE_COLLECT;

CALLGRIND_DUMP_STATS;
CALLGRIND_STOP_INSTRUMENTATION;

// Rest of the program

重新编译,并使用以下命令启动 Callgrind:

valgrind --tool=callgrind --instr-atstart=no --collect-atstart=no <PROGRAM> <PROGRAM_OPTIONS>

请注意,此方法将生成 2 个 Callgrind 输出文件 - 第一个由DUMP_STATS宏创建,第二个在程序退出时生成。DUMP_STATS使用后将所有计数器清零,这意味着第二个日志将报告 0 个事件。

在活动检测块中,您还可以多次切换收集并为每个块转储收集的统计信息。

于 2018-03-02T06:37:53.740 回答