0. 简介
作为一个程序而言,benchmark是非常关键的一个衡量指标,无论是程序算法的指标还是程序运行性能的指标,这些我们都可以去完成衡量。对于性能衡量而言google benchmark无疑是一个比较好的选择
1. google benchmark安装
1.1 下载地址
https://github.com/google/benchmark
1.2 编译安装
登录 linux环境,执行以下命令,进行编译安装:
git clone https://github.com/google/benchmark.git
cd benchmark
git clone https://github.com/google/googletest.git
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=RELEASE
make
sudo make install
2. 代码编写
创建一个C++源文件,并编写包含基准测试函数的代码。例如,创建一个名为benchmark_example.cpp的文件,并编写如下内容:
#include <benchmark/benchmark.h>
static void BM_MyFunction(benchmark::State& state) {
// 在这里编写您要测试的代码
for (auto _ : state) {
// 执行您的代码
}
}
BENCHMARK(BM_MyFunction);
BENCHMARK_MAIN();
在上述示例中,BM_MyFunction是您要测试的函数。
然后我们可以使用C++编译器编译您的代码,并链接Google Benchmark库。
g++ benchmark_example.cpp -o benchmark_example -lbenchmark -lpthread
如果是cmakelist,则可以使用
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
# benchmark依赖thread线程库
add_library(benchmark STATIC IMPORTED)
set_property(TARGET benchmark PROPERTY IMPORTED_LOCATION /usr/local/lib/libbenchmark.a)
add_executable(demo demo.cpp)
target_link_libraries(demo
benchmark
)
install(TARGETS
demo
DESTINATION "bin/"
)
2.1 基础代码调用测试
们可以看到每一个benchmark测试用例都是一个类型为std::function<void(benchmark::State&)>的函数,其中benchmark::State&负责测试的运行及额外参数的传递。
测试用例编写完成后我们需要使用BENCHMARK(<function_name>);将我们的测试用例注册进benchmark,这样程序运行时才会执行我们的测试。
最后是用BENCHMARK_MAIN();替代直接编写的main函数,它会处理命令行参数并运行所有注册过的测试用例生成测试结果。
#include <benchmark/benchmark.h>
#include <vector>
#include <array>
constexpr int len = 6;
std::vector<int> vec{1, 2, 3, 4, 5, 6};
std::array<int, len> array{1, 2, 3, 4, 5, 6};
// benchmark::State &state用于维护测试上下文信息,以及控制迭代次数
static void vector_test(benchmark::State &state)
{
for (auto _ : state)
{
vec[0];
vec[1];
vec[2];
vec[3];
vec[4];
vec[5];
}
}
static void array_test(benchmark::State &state)
{
for (auto _ : state)
{
array[0];
array[1];
array[2];
array[3];
array[4];
array[5];
}
}
// 注册测试用例
BENCHMARK(vector_test);
BENCHMARK(array_test);
// benchmark的主函数
BENCHMARK_MAIN();
结果格式如下:
Load Average: 0.43, 0.25, 0.10
------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------
vector_test 6.81 ns 6.81 ns 102373755
array_test 13.6 ns 13.6 ns 51227934
2.2 传参调用测试
上面的测试用例都只接受一个benchmark::State&类型的参数,所以我们可以使用BENCHMARK宏生成的对象的Arg方法来完成参数的传递。
传递进来的参数会被放入state对象内部存储,通过range方法获取,调用时的参数0是传入参数的需要,对应第一个参数
#include <benchmark/benchmark.h>
#include <vector>
#include <array>
static void bench_array_ring_insert_int(benchmark::State& state)
{
auto length = state.range(0);
auto ring = ArrayRing<int>(length);
for (auto _: state) {
for (int i = 1; i <= length; ++i) {
ring.insert(i);
}
state.PauseTiming();
ring.clear();
state.ResumeTiming();
}
}
// 下面我们生成测试插入10次的测试用例
BENCHMARK(bench_array_ring_insert_int)->Arg(10);
static void bench_array_ring_insert_int(benchmark::State& state)
{
auto ring = ArrayRing<int>(state.range(0));
for (auto _: state) {
for (int i = 1; i <= state.range(1); ++i) {
ring.insert(i);
}
state.PauseTiming();
ring.clear();
state.ResumeTiming();
}
}
BENCHMARK(bench_array_ring_insert_int)->Args({10, 10});
// benchmark的主函数
BENCHMARK_MAIN();
2.3 模板类的调用测试
如果针对每一种情况写测试函数,显然违反了DRY原则,因为除了vector的类型参数不同,其他代码几乎是完全一样的。
#include <benchmark/benchmark.h>
#include <vector>
#include <array>
template <typename T, std::size_t length, bool is_reserve = true>
void bench_vector_reserve(benchmark::State& state)
{
for (auto _ : state) {
std::vector<T> container;
if constexpr (is_reserve) {
container.reserve(length);
}
for (std::size_t i = 0; i < length; ++i) {
container.push_back(T{});
}
}
}
BENCHMARK_TEMPLATE(bench_vector_reserve, std::string, 100);
// benchmark的主函数
BENCHMARK_MAIN();