Profiler01-宏定义
拆分Profiler中的宏定义和展开这块的内容 ——
开启 / 关闭 Profiler 模块
Profiler 提供了两层开关:
- 编译期开关:决定埋点代码是否被编译进目标文件
- 运行时开关:决定 ProfilerSession 当前是否采集事件
编译期
首先在顶层的cmake中定义了选项
option(
CHIKA_ENABLE_PROFILING
"Compile ChikaProfiler instrumentation into engine targets"
ON
)接着通过
add_compile_definitions(CHIKA_ENABLE_PROFILING=$<BOOL:${CHIKA_ENABLE_PROFILING}>)将其转化成宏,然后在ProfilerMacros中根据宏定义选择行为
运行时
通过设置在初始化 Profiler 的时候传入的 enabled 参数进行设定 或者运行时进行 SetEnabled —— 如果不启用,则在运行时不采集事件 —— 它仍会执行宏展开后的代码入口,但是运行时会因为没启用而“早退”
宏定义
主要可以阅读源码ProfilerMacros.hpp
先在开头兜底 ——
#ifndef CHIKA_ENABLE_PROFILING
#define CHIKA_ENABLE_PROFILING 0
#endif如果构建系统没有定义 CHIKA_ENABLE_PROFILING,那就默认认为它是 0
接着是做了一个 token 拼接
#define CHIKA_PROFILE_CONCAT_IMPL(lhs, rhs) lhs##rhs
#define CHIKA_PROFILE_CONCAT(lhs, rhs) CHIKA_PROFILE_CONCAT_IMPL(lhs, rhs)此处使用两层宏包裹 —— ## 两侧的参数在拼接前不会按普通方式继续展开。cppreference 对扫描与替换规则也说明:宏参数在进入 replacement-list 前会被扫描,但 # 和 ## 是例外,它们处理的是未先宏展开的参数
所以实际上是让外层先把__LINE__等先进行展开,然后再在内层进行拼接
启用时
正常的定义了CHIKA_PROFILE_SCOPE等东西,里面涉及到的模块具体做了什么会在之后说明
此处使用了__LINE__做一个拼接,这样大概率在一个作用域内定义变量的时候不会重复(除非有压行的疯子在同一行定义多个CHIKA_PROFILE_SCOPE
关闭时
使用((void)0),避免空宏可能会出现的一些语法玄学问题。
宏展开
CHIKA_PROFILE_SCOPE
它展开后表层上做两件事 ——
- 定义一个 static const uint32_t 变量,用来保存当前 profile name 对应的 ID
- 定义一个 ProfilerScope 局部对象
(具体等后面对于ProfilerScope等的代码进行说明
同时使用static const可以使得函数执行到此处的时候不会重复申请空间生声明局部变量,比较有效率
CHIKA_PROFILE_FUNCTION
一个小的简写 —— 把CHIKA_PROFILE_SCOPE(__func__);化简一下
CHIKA_PROFILE_FRAME
创建一个 ProfilerFrameScope 局部对象,不需要名字,只需要 FrameID 即可
CHIKA_PROFILE_COUNTER
用于记录运行时的数值
CHIKA_PROFILE_INSTANT
结构上和 CHIKA_PROFILE_COUNTER 非常相似,同时额外说一下使用 do/while 包裹(其实总觉得在哪一篇写过)
使得多行语句表现得像单条语句,这样不容易破坏外部调用处的结构