Profiler04-Event 数据
定义 ProfilerEvent —— 把一次 profiler 的行为打包,形成一个固定格式,而不是随意乱写的数据(
数据定义
主体事件定义
struct alignas(8) ProfilerEvent
{
uint64_t timestampNs = 0;
uint64_t frameId = kInvalidProfilerFrameId;
uint64_t payload = 0;
uint32_t threadId = 0;
uint32_t nameId = kInvalidProfilerNameId;
ProfilerEventType type = ProfilerEventType::Instant;
ProfilerEventFlags flags = ProfilerEventFlags::None;
uint16_t reserved = 0;
};其中 ——
timestampNs时间戳,记录事件发生的时间frameId记录事件发生的那一帧payload不同的事件有不同的语义 —— 记录数据使用threadId记录事件来自的线程IDnameId记录事件关联的名字(只记录 id 避免字符串转发)type表示事件类型flags表示事件附加信息reserved为未来的拓展预留一块
事件类型
enum class ProfilerEventType : uint8_t
{
// 帧标记,例如 frame begin / frame end。
FrameMark,
// CPU zone 开始。
ZoneBegin,
// CPU zone 结束。
ZoneEnd,
// 数值采样。
Counter,
// 瞬时事件。
Instant,
// 线程命名事件。
ThreadName,
};直接在注释说明,特别的
- Counter 只是数值的快照
- Instant 是一个时刻的标记点
此处使用uint8_t,大小为一个字节
事件附加标记
enum class ProfilerEventFlags : uint8_t
{
// 没有特殊标记。
None = 0,
// 当前 FrameMark 是 frame begin。
// 1u << 0u = 0000'0001
FrameBegin = 1u << 0u,
// payload 里面装的是 double 的 bit pattern。
// 1u << 1u = 0000'0010
FloatingPointPayload = 1u << 1u,
};可以未来接上新的拓展标记之类的,不过当前代码里主要用到的是区分 ——
FrameMark是否是 frame beginpayload是否是 double
不过当前的代码中没有为 ProfilerEventFlags 定义 operator| / operator&。因此从这个模块本身来看,它更像是单 flag 使用;如果未来需要组合多个 flag,可以补充对应的位运算重载或显式转换。
小细节
Event 对齐
在声明 ProfilerEvent 的时候使用了 alignas(8) 使得结构体至少按照 8 字节对齐,而 ——
uint64_t timestampNs 8
uint64_t frameId 8
uint64_t payload 8
uint32_t threadId 4
uint32_t nameId 4
ProfilerEventType 1
ProfilerEventFlags 1
uint16_t reserved 2
------------------------
合计 36那么向上补齐之后就是40字节(在源码中也有断言确认
相当于固定布局,这样在后期修改结构体字段的时候可以在编译期快速得知
trivially copyable
我们还有一个断言,就是static_assert(std::is_trivially_copyable_v<ProfilerEvent>);
此处的意思是“让这个对象可以像一块普通字节数据一样复制” —— 因为后期我们需要频繁的在buffer 里移动 / 拷贝,所以强调避免在事件对象里引入 std::string、std::vector 等带动态资源的成员,这样再移动和拷贝的时候比较贵
Pack / Unpack
此处略有意思,先看源码
inline uint64_t PackProfilerDouble(double value)
{
return std::bit_cast<uint64_t>(value);
}
inline double UnpackProfilerDouble(uint64_t value)
{
return std::bit_cast<double>(value);
}此处是一个 pack / unpack double 类型的例子,看上去我们似乎是在做一个类型转化 —— double -> uint64_t 以及 uint64_t -> double,但是这个平常的 static_cast 不太一致,那个作为类型转化,在浮点到整型的时候会取整(比如3.14->3)。但是通过 bit_cast 的语义 —— “这就是这么些比特位,我只是"换一种形式存储"而已,不要去动里面的数据”