c++谷歌测试框架

google/benchmark是一个由Google开发的基于googletest框架的c++ benchmark工具,它易于安装和使用,并提供了全面的性能测试接口。 Benchmark 是 Google 一个用来对代码片段进行基准测试的库,类似单元测试。 一、Google benchmark的安装

google/benchmark是一个由Google开发的基于googletest框架的c++ benchmark工具,它易于安装和使用,并提供了全面的性能测试接口。

Benchmark 是 Google 一个用来对代码片段进行基准测试的库,类似单元测试。

一、Google benchmark的安装

google/benchmark基于c++11标准和googletest框架,所以安装前需要先做一些准备工作。

1、首先是安装g++和cmake

ubuntu:sudo apt install cmake g++

2、查看g++的版本,确保版本在5.0以上,否则可能不会很好的支持C++11的某些特性

3、随后是googletest框架,你可以选择单独安装,不过这里我选择将其作为benchmark源码树的依赖而不单独安装它,因为benchmark在编译安装时需要googletest但是在使用时并不需要,为了篇幅我们选择后者。

4、源码安装:登录 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

特别注意:头文件会被安装至/usr/local/include,库文件会安装至/usr/local/lib

二、google/benchmark的简单使用

接下来,我们将会对比三种访问`std::array`容器内元素方法的性能,进而演示benchmark的使用方法。

1、代码展示:

#include <benchmark/benchmark.h>
#include <array>

constexpr int len = 6;

// constexpr function具有inline属性,你应该把它放在头文件中
constexpr auto my_pow(const int i)
{
    return i * i;
}

// 使用operator[]读取元素,依次存入1-6的平方
static void bench_array_operator(benchmark::State& state)
{
    std::array<int, len> arr;
    constexpr int i = 1;
    for (auto _: state) {
        arr[0] = my_pow(i);
        arr[1] = my_pow(i+1);
        arr[2] = my_pow(i+2);
        arr[3] = my_pow(i+3);
        arr[4] = my_pow(i+4);
        arr[5] = my_pow(i+5);
    }
}
BENCHMARK(bench_array_operator);

// 使用at()读取元素,依次存入1-6的平方
static void bench_array_at(benchmark::State& state)
{
    std::array<int, len> arr;
    constexpr int i = 1;
    for (auto _: state) {
        arr.at(0) = my_pow(i);
        arr.at(1) = my_pow(i+1);
        arr.at(2) = my_pow(i+2);
        arr.at(3) = my_pow(i+3);
        arr.at(4) = my_pow(i+4);
        arr.at(5) = my_pow(i+5);
    }
}
BENCHMARK(bench_array_at);

// std::get<>(array)是一个constexpr function,它会返回容器内元素的引用,并在编译期检查数组的索引是否正确
static void bench_array_get(benchmark::State& state)
{
    std::array<int, len> arr;
    constexpr int i = 1;
    for (auto _: state) {
        std::get<0>(arr) = my_pow(i);
        std::get<1>(arr) = my_pow(i+1);
        std::get<2>(arr) = my_pow(i+2);
        std::get<3>(arr) = my_pow(i+3);
        std::get<4>(arr) = my_pow(i+4);
        std::get<5>(arr) = my_pow(i+5);
    }
}
BENCHMARK(bench_array_get);

BENCHMARK_MAIN();

注:constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。

我们可以看到每一个benchmark测试用例都是一个类型为std::function<void(benchmark::State&)>的函数,其中benchmark::State&负责测试的运行及额外参数的传递。

随后我们使用for (auto _: state) {}来运行需要测试的内容,state会选择合适的次数来运行循环,时间的计算从循环内的语句开始,所以我们可以选择像例子中一样在for循环之外初始化测试环境,然后在循环体内编写需要测试的代码。

测试用例编写完成后我们需要使用BENCHMARK(<function_name>);将我们的测试用例注册进benchmark,这样程序运行时才会执行我们的测试。

最后是用BENCHMARK_MAIN();替代直接编写的main函数,它会处理命令行参数并运行所有注册过的测试用例生成测试结果。

示例中大量使用了constexpr,这是为了能在编译期计算出需要的数值避免对测试产生太多噪音。

2、编译测试程序

g++ -Wall -std=c++14 benchmark_test.cpp -pthread -lbenchmark

benchmark需要链接libbenchmark.so,所以需要指定-lbenchmark,此外还需要thread的支持,因为libstdc++不提供thread的底层实现,我们需要pthread。另外不建议使用-lpthread,官方表示会出现兼容问题,在我这测试也会出现链接错误。注意文件名一定要在-lbenchmark前面,否则编译会失败,具体参见:https://github.com/google/benchmark/issues/619

如果你是在Windows平台使用google/benchmark,那么你需要额外链接shlwapi.lib才能使benchmark正常编译和运行。

常用的命令行参数设置:

命令行参数的使用:
    ./demo(./a.out) --benchmark_list_tests=true

(1)列出执行的测试case

--benchmark_list_tests={true|false}    注:默认为false

(2)设置测试case过滤器

--benchmark_filter=<regex>    默认为:“.”

下面我们设置命令行参数:--benchmark_filter=BM_demo,输出如下:

(3) 指定命令行输出格式(console、json、csv三种)

--benchmark_format=<console | json | csv>    默认为:console

我们改变输出格式为json,部分输出截图如下:

(4)指定输出到指定文件

--benchmark_out=<filename>    默认为:“”

设置命令行参数--benchmark_out=/home/benchmark_test/1.txt,会在指定位置生成1.txt输出文件

3、运行程序,查看测试结果   注:时钟时间 = 阻塞时间 + 就绪时间 + 运行时间(也就是CPU时间)

特别注意:如果运行过程中,有显示的警告信息表示在当前系统环境有一些噪音(例如其他在运行的程序)可能导致结果不太准确,但是注意这并不影响我们的测试。

测试结果与预期基本相符,std::get最快,at()最慢。

4、指定迭代次数的案例

#include <benchmark/benchmark.h>
#include <iostream>
#include <string>
using namespace std;

void demo()
{
    string str = "hello world";
    str.size();
}

static void BM_demo(benchmark::State& state) {
    for (auto _ : state)
        demo();
}
// Register the function as a benchmark
BENCHMARK(BM_demo)->Iterations(1000); //指定BM_demo函数中,for循环的迭代次数
BENCHMARK_MAIN(); //程序入口

测验结果:

 

知秋君
上一篇 2024-08-22 17:48
下一篇 2024-08-22 17:12

相关推荐