在robotic_arm的third_party已经集成了benchmark,只需在相关测试用例代码的CmakeLists.txt添加如下内容:
target_link_libraries(xxx PRIVATE benchmark pthread)
存在以下模式样例:
#include <benchmark/benchmark.h>
#include <chrono>
#include <thread>
void BM_DemoSleep(benchmark::State& state) {
for (auto _ : state){
//待测试的代码
}
}
BENCHMARK(BM_DemoSleep); // 注册要测试的函数对象
BENCHMARK_MAIN(); // main函数,运行benchmark初始化和执行
#include <benchmark/benchmark.h>
#include <chrono>
#include <thread>
void BM_DemoSleep(benchmark::State& state) {
for (auto _ : state){
std::this_thread::sleep_for(std::chrono::nanoseconds(1000)); //待测试的代码
}
}
void BM_DemoSleep1(benchmark::State& state, int id) {
std::cout << "id:"<< id << std::endl;
for (auto _ : state){
std::this_thread::sleep_for(std::chrono::nanoseconds(1000));
}
}
int main(int argc, char** argv) {
::benchmark::Initialize(&argc, argv); // 初始化Benchmark
if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
// 使用函数指针注册
::benchmark::RegisterBenchmark("BM_DemoSleep", &BM_DemoSleep);
// 使用Lamba函数注册
::benchmark::RegisterBenchmark("BM_DemoSleep1", [](benchmark::State& state){
for (auto _ : state){
std::this_thread::sleep_for(std::chrono::nanoseconds(1000));
}
});
// 使用带参数的函数指针注册
int id = 10;
::benchmark::RegisterBenchmark("BM_DemoSleep2", &BM_DemoSleep1, id);
::benchmark::RunSpecifiedBenchmarks(); // 运行
::benchmark::Shutdown();
}
class BMDemo : public benchmark::Fixture {
public:
void SetUp(const benchmark::State& state) {
id_ = 2;
}
void TearDown(const ::benchmark::State& state) {
id_ = 0;
}
int GetId() const {return id_;};
private:
int id_{0};
};
BENCHMARK_F(BMDemo, Test0)(benchmark::State& state) {
for (auto _ : state) {
std::this_thread::sleep_for(std::chrono::milliseconds(GetId())); // test code
}
}
BENCHMARK_F(BMDemo, Test1)(benchmark::State& state) {
for (auto _ : state) {
std::this_thread::sleep_for(std::chrono::milliseconds(GetId())); // test code
}
}
原理:BENCHMARK_F(BMDemo, Test0)(benchmark::State& state){},会创建一个BMDemo_Test0_Benchmark的类,继承至BMDemo,然后实现BMDemo_Test0_Benchmark::BenchmarkCase(benchmark::State& state){}成员函数;在Fixture的Run方法中会一次调用SetUp->BenchmarkCase->TearDown
接口名称 | 作用 |
---|---|
Benchmark* Arg(int64_t x); | 向Benchmark对象的std::vector<std::vector<int64_t> > args_添加一个元素(元素为vector{x}) |
Benchmark* Range(int64_t start, int64_t limit); | Benchmark* Range(int64_t start, int64_t limit); |
Benchmark* DenseRange(int64_t start, int64_t limit, int step = 1); | Benchmark* DenseRange(int64_t start, int64_t limit, int step = 1); |
Benchmark* Args(const std::vector<int64_t>& args); | 向Benchmark对象的std::vector<std::vector<int64_t> > args_添加一个元素(元素为args) |
Benchmark* ArgPair(int64_t x, int64_t y) | 向Benchmark对象的std::vector<std::vector<int64_t> > args_添加一个元素(元素为vector{x,y}) |
void BM_Arg(benchmark::State& state) {
std::cout << "arg1:" << state.range(0) << "\n"; // state.range(0)获取参数
for (auto _ : state) {
std::this_thread::sleep_for(std::chrono::milliseconds(state.range(0)));
}
}
int main(int argc, char** argv) {
::benchmark::Initialize(&argc, argv);
if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
::benchmark::RegisterBenchmark("BM_Arg", &BM_Arg)->Arg(10); // ->Arg()设置单个参数
::benchmark::RegisterBenchmark("BM_Arg", &BM_Arg)->Arg(10)->Arg(11); //分别以10,11参数运行BM_Arg
::benchmark::RunSpecifiedBenchmarks();
::benchmark::Shutdown();
}
会有如下策略(简单的将如果没有明确设置iteration,会使用相应的计算方法(会使用到min_time),递增迭代次数然后以最后一个确定的结果report):
我们可能逐渐增加基准的长度(迭代次数),直到我们确定结果是重要的。 一旦我们这样做了,我们就会报告最后的结果并退出。 请注意,如果有重复,则迭代计数仅为第一次重复计算,其他重复仅使用该预先计算的迭代计数。
::benchmark::RegisterBenchmark("BM_Arg", &BM_Arg)->Iterations(10); // 迭代执行10次,也就是for(auto _ : state){}循环会迭代10次
::benchmark::RegisterBenchmark("BM_Arg", &BM_Arg); // 会按照计算规则,迭代n次
指的是整个函数对象调用多少次,默认值是1
::benchmark::RegisterBenchmark("BM_Arg", &BM_Arg)->Iterations(10)->Repetitions(3); // 迭代执行10次,也就是for(auto _ : state){}循环会迭代10次; 重复调用3次
Benchmark* Unit(TimeUnit unit);
设置显示时间单位:
kNanosecond, kMicrosecond, kMillisecond, kSecond.
Benchmark* Threads(int t); | 设置多少个线程运行测试(线程函数中运行注册的函数对象) |
Benchmark* ThreadRange(int min_threads, int max_threads); | 类似args_, 以min_threads为起点,倍率为2,终点为 max_threads,向thread_counts_添加元素比如: Range(1, 16), Threads(1)->Threads(2)-> Threads(4)-> Threads(8)-> Threads(16),分别以1、2、4、8、16个线程进行测试 |
Benchmark* DenseThreadRange(int min_threads, int max_threads, int stride = 1); | 类似args_, 以min_threads为起点,步长为1,终点为 max_threads,向thread_counts_添加元素,DenseThreadRange(1, 8, 3), 1、4、7、8个线程进行测试 |
void BM_Arg(benchmark::State& state) {
for (auto _ : state) {
auto start = std::chrono::high_resolution_clock::now();
// Simulate some useful workload with a sleep
std::this_thread::sleep_for(sleep_duration);
auto end = std::chrono::high_resolution_clock::now();
auto elapsed_seconds =
std::chrono::duration_cast<std::chrono::duration<double>>(
end - start);
state.SetIterationTime(elapsed_seconds.count());
}
}
::benchmark::RegisterBenchmark("BM_Arg", &BM_Arg)->UseManualTime();
会统计每次的结果,然后输出分析结果:
mean: 平均值、median: 中值、stddev: 标准差、cv:标准差/平均值
自定义分析结果,比如最小值,最大值
接口:
typedef double(StatisticsFunc)(const std::vector<double>&);
Benchmark* ComputeStatistics(std::string name, StatisticsFunc* statistics,
StatisticUnit unit = kTime);
示例:
::benchmark::RegisterBenchmark("BM_Arg", &BM_Arg)->Iterations(10)->Repetitions(10)->Unit(benchmark::kMillisecond)
->ComputeStatistics("max", [](const std::vector<double>& v)->double{
return *std::max_element(v.begin(), v.end());
}, benchmark::kTime)
->ComputeStatistics("min", [](const std::vector<double>& v)->double{
return *std::min_element(v.begin(), v.end());
}, benchmark::kTime);
–benchmark_out_format=<console|json|csv>
定义输出格式
–benchmark_out=
定义文件名
–benchmark_filter=
定义过滤规则(正则表达式)
–benchmark_repetitions=n
定义重复次数
–benchmark_report_aggregates_only={true|false}
上报内容是否只上报聚合内容(省略每次repetition的内容)
–benchmark_display_aggregates_only={true|false}
屏幕输出内容是否只上报聚合内容(省略每次repetition的内容)
模版方法和模版Fixture本文无介绍,相关使用方法可以参考google benchmark的github地址