C++ Google benchmark testing and analysis

0. Introduction

As a program, benchmark is a very critical measurement indicator. Whether it is an indicator of program algorithm or an indicator of program running performance, we can measure all of these. Google benchmark is undoubtedly a better choice for performance measurement.

1. google benchmark installation

1.1 Download address

https://github.com/google/benchmark

1.2 Compile and install

Log in to the Linux environment and execute the following commands to compile and install:

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. Code writing

Create a C++ source file and write code that contains the benchmark function. For example, create a file called benchmark_example.cpp and write the following content:

#include <benchmark/benchmark.h>

static void BM_MyFunction(benchmark::State& state) {
    
    
    // 在这里编写您要测试的代码
    for (auto _ : state) {
    
    
        // 执行您的代码
    }
}

BENCHMARK(BM_MyFunction);

BENCHMARK_MAIN();

In the above example, BM_MyFunction is the function you want to test.

We can then compile your code using the C++ compiler and link against the Google Benchmark library.

g++ benchmark_example.cpp -o benchmark_example -lbenchmark -lpthread

If it is cmakelist, you can use

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 Basic code call test

We can see that each benchmark test case is a function of type std::function<void(benchmark::State&)>, where benchmark::State& is responsible for running the test and passing additional parameters.

After the test case is written, we need to use BENCHMARK(<function_name>); to register our test case into the benchmark, so that our test will be executed when the program is running.

Finally, use BENCHMARK_MAIN(); instead of the directly written main function, which will process the command line parameters and run all registered test cases to generate test results.

#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();

The result format is as follows:

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 Parameter calling test

The above test cases only accept one parameter of type benchmark::State&, so we can use the Arg method of the object generated by the BENCHMARK macro to complete the parameter transfer.


The parameters passed in will be put into the internal storage of the state object and obtained through the range method. Parameter 0 when calling is the need for the parameters passed in, corresponding to the first parameter.

#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 Call test of template class

If you write a test function for each situation, it obviously violates the DRY principle, because except for the different type parameters of the vector, the other codes are almost exactly the same.

#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();

3. Use Benchmark interface

…For details, please refer toGuyueju

Guess you like

Origin blog.csdn.net/lovely_yoshino/article/details/131175020