microbenchmark 笔记
背景
Fluent Bit 在代码中有将时间转化为浮点数(单位秒)再和 0.0
对比判断时间是否为 flb_time_zero
的初始值。在这个场景下用整型运算应该更快,但在提交 PR 前还是需要用数据说话,所以先来 benchmark 一下。
环境准备
这次我使用了 Google 维护的 benchmark 库,使用 Conan 管理依赖并用 CMake 构建。先安装一下:
sudo port install pipx cmake
pipx install conan
然后准备 conanfile.txt
和 CMakeLists.txt
,可以参考我的仓库 benchmarks。
benchmark 单元编写
static void BM_Nano(benchmark::State& state) {
struct flb_time out_time = {0};
flb_time_zero(&out_time);
bool flag;
for (auto _ : state) {
flag = flb_time_to_nanosec(&out_time) == 0L;
}
}
只要把需要测试的代码放到 for 循环内就会自动测试这段代码的性能。但测试结果出来后有点奇怪:
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
BM_Nano 0.249 ns 0.249 ns 1000000000
BM_Sec 0.248 ns 0.248 ns 1000000000
运行了几次时间上都差不多,难道现代 CPU 的浮点性能这么强了?感觉有点不对劲,试着加上一个 BM_None
测试做对比,代码里直接赋值 true 不做任何运算:
for (auto _ : state) {
flag = true;
}
测试出来结果和其他两个都相同,数据肯定是有问题了。
排查原因
打开 Compiler Explorer,添加 benchmark 库,一看编译出的汇编就明白了。
整个 for 循环都被优化掉了。。。
解决方案
在网上搜索如何阻止编译器优化某部分代码,发现最简单且相对通用的方式是使用 volatile 关键字,那给 flag 变量加上 volatile 试试。
汇编变长了,但还是没生成浮点运算。看来还不够 volatile,再加到函数参数上试试:
总算看到要 benchmark 的函数被上色了!再跑一遍 benchmark 试试?
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
BM_None 0.242 ns 0.242 ns 1000000000
BM_Nano 0.372 ns 0.371 ns 1000000000
BM_Sec 1.11 ns 1.11 ns 623846997
果然如我所料,浮点运算的性能还是要差一些的,于是提交 PR #4535,搞定收工。