c++ 使用 google benchmark

在低延迟场景中,我们对性能有极致的要求。为了方便对比不同函数的开心,需要借助一些测试手段。这些测试的基本流程是:

  1. 在函数调用开始是计算 rdtsc 初始值
  2. 函数调用结束后,计算 rdtsc 的差值
  3. 循环以上流程若干次
  4. 最终得到一个平均的函数开销时间

整个测试流程其实是非常的标准化,我们完全可以利用一些框架进行快速的测试。比如我现在使用的 google benchmark

安装

1
2
3
4
git clone https://github.com/google/benchmark.git
git clone https://github.com/google/googletest.git benchmark/googletest
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=RELEASE ../benchmark

测试

函数原型

测试有两部分构成

  • 测试函数,原型为 std::function<void(benchmark::State&)>,然后使用宏命令 BENCHMARK(func) 将其注册到主程序。google benchmark 会循环运行该函数,并统计相关指标
  • 主程序入口:BENCHMARK_MAIN()

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <benchmark/benchmark.h>
#include <array>
#include <utils/nanotime.hpp>

using namespace falcon;

//-----------------------------------------------------------------------------
void now_ns(benchmark::State& state)
{
    for (auto _: state)
    {
        nanotime_t::ns();
    }
}
BENCHMARK(now_ns);

void now_sysns(benchmark::State& state)
{
    for (auto _: state)
    {
        nanotime_t::sysns();
    }
}
BENCHMARK(now_sysns);

void now_ntime(benchmark::State& state)
{
    for (auto _: state)
    {
        nanotime_t::ntime();
    }
}
BENCHMARK(now_ntime);

void ns2ntime(benchmark::State& state)
{
    for (auto _: state)
    {
        auto ns = nanotime_t::ns();
        nanotime_t::ns2ntime(ns);
    }
}
BENCHMARK(ns2ntime);

void ns2str(benchmark::State& state)
{
    for (auto _: state)
    {
        auto ns = nanotime_t::ns();
        nanotime_t::ns2str(ns);
    }
}
BENCHMARK(ns2str);

void ns2str_slow(benchmark::State& state)
{
    for (auto _: state)
    {
        auto ns = nanotime_t::ns();
        nanotime_t::to_str_slow(ns);
    }
}
BENCHMARK(ns2str_slow);

void ns2datetimestr(benchmark::State& state)
{
    for (auto _: state)
    {
        auto ns = nanotime_t::ns();
        nanotime_t::ns2datetimestr(ns);
    }
}
BENCHMARK(ns2datetimestr);

void strftime(benchmark::State& state)
{
    for (auto _: state)
    {
        auto ns = nanotime_t::ns();
        nanotime_t::strftime(ns);
    }
}
BENCHMARK(strftime);
//-----------------------------------------------------------------------------


///////////////////////////////////////////////////////////////////////////////
BENCHMARK_MAIN();
///////////////////////////////////////////////////////////////////////////////

结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
2024-04-21T13:52:30+08:00
Running ./test_benchmark
Run on (20 X 4800 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 1280 KiB (x10)
  L3 Unified 25600 KiB (x1)
Load Average: 1.13, 1.27, 1.62
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
---------------------------------------------------------
Benchmark               Time             CPU   Iterations
---------------------------------------------------------
now_ns               5.49 ns         5.49 ns    107478294
now_sysns            11.3 ns         11.3 ns     62195134
now_ntime            5.43 ns         5.43 ns    130701288
ns2ntime             5.47 ns         5.47 ns    123446131
ns2str               15.0 ns         15.0 ns     47280993
ns2str_slow          60.9 ns         60.9 ns     11144495
ns2datetimestr        460 ns          460 ns      1577622
strftime            15785 ns        15785 ns        44630

从上述结果可以比较清楚的知道,我们的函数调用 ns 的开销对比系统调用 sysns 是非常小的,但在转化成字符串的时候,不同的设计是有明显的差异。其中 ns2str 是最快的,这主要是因为我们使用来一个针对小对象设计的 Str<N> 类。

william 支付宝支付宝
william 微信微信
0%