带有注释(pragma)的仪器 C 代码用于性能跟踪

Posted

技术标签:

【中文标题】带有注释(pragma)的仪器 C 代码用于性能跟踪【英文标题】:Instrument C code with annotations (pragmas) for performance tracking 【发布时间】:2016-04-14 06:09:04 【问题描述】:

我想测量 C 代码 中非常具体的部分的时间(或出现次数)(它们可能仅限于某些函数中的几条指令)。一个目的是跟踪多个代码修订版的本地性能改进或回归。

我知道我可以为此目的定义宏。但是是否有任何工具已经以一种更侵入性更小的方式做到了这一点?使用注释 (#pragma) 将是完美的:

void func_to_profile()

    /* Some instructions */
    ...


#pragma profile foo start
    /* A part of the code to track */
    ...
#pragma profile foo stop


    /* More instructions */
    ...


#pragma profile bar start
    /* Another part to measure */
    ...
#pragma profile bar stop

理想情况下,在运行结束时,该工具会显示每个小节的累计经过时间。例如:

-- [foo] cumulated time: 42s
-- [bar] cumulated time: 7s

是否有任何现有的工具已经可以做到这一点,或者我别无选择,只能开发自己的 GCC 插件

【问题讨论】:

#pragma和使用宏有什么区别?除了 pragma 没有机会移植,而宏可以移植? C 行和优化的 asm 输出之间没有直接映射。强制编译器在两个障碍之间做某部分工作可能会导致代码明显变差。最好的办法是查看 CPU 性能计数器(例如 linux perf)来查找执行热点。在 x86 上,即使是最轻量级的计时工具 (CPUID) 也有大约 20 个周期的开销,因此仅测量几条指令就太重了。用它来测量一个循环的所有迭代,而不是单独测量。 如果你这样做是为了找到加速,你需要换个思路。您肯定在代码中有多个加速机会,因此仅找到其中一个是不够的。它们都是热点的可能性很小。试试method that speedups cannot hide from @Art 使用宏,我必须手动包含包含宏定义的标题 + 一个宏来指定我要激活分析。我还必须在程序结束时显式调用一个函数来打印结果,在某些情况下,还必须在开始时调用一个初始化函数。使用插件,所有调用可能会自动插入编译时间,只留下编译指示开始/停止。 @PeterCordes 你是绝对正确的,CPU 性能计数器在某些情况下可能非常适合查找热点。perf 做得很好。当然,我不想以几个周期的粒度来测量时间,在最内部的循环中甚至更少。我更新了我的问题,我想在这里实现的是跟踪某些代码部分的性能以进行比较(跨代码版本,例如检测本地性能改进或回归)。这些部分已被确定为耗时且处于关键路径上(此识别工作可能已使用 perf 完成)。 【参考方案1】:

perf record 对于像核心时钟周期这样的事件会将事件累积到指令中。但是,这并不精确:获取事件计数的指令并不总是那些本身很慢的指令,只是在附近,例如等待缓慢的事情发生。但足够接近,可能有用。

您似乎只需要查看每个 insn 或每行 C 的计数(通过调试信息进行映射)并查看相对计数何时发生变化。

这应该可以识别更改何时会使 asm 的某个部分运行更慢:与该源行关联的 insn 的 perf 事件计数的相对份额将更高。 (+/- 很多挥手,因为 C 行并不总是直接映射到 asm 指令,尤其是当优化重组一些分支逻辑、自动向量化等时)


也许可以编写一个自动化测试程序,在perf record 下运行您的代码,然后将perf report 数据按摩成某种格式,以便在比较源版本时进行比较/跟踪。

【讨论】:

以上是关于带有注释(pragma)的仪器 C 代码用于性能跟踪的主要内容,如果未能解决你的问题,请参考以下文章

C之 #pragma(二十二)

库导入:#pragma 注释 VS Visual Studio 项目输入

C语言学习笔记--#error #line 和 #pragma 的使用

20大塑料性能测试仪器大汇总,高分子材料研发必备!!!

如果行程计数不恒定,为啥我的#pragma-unrolled 循环的性能会下降?

Unity #pragma multi_compile说明