目的
google/Benchmark の導入、簡単な使い方を説明する。
概ねリポジトリに記載のある内容だが、 README.md
も十分に親切というわけではないため、理解を深めるためにもこちらで自分なりにまとめる。
参考文献
リポジトリに含まれるドキュメントを参照すれば十分利用できる。またヘッダファイルにも多くのコメントが含まれている。
- User Guide (google/Benchmark)
- その他のドキュメントは /docs (google/Benchmark) に格納されている。
- 機能の全容を調べるにはヘッダファイル /include/benchmark/benchmark.h を参照する。
開発環境
以下の環境で動作確認を行った。
- Ubuntu 22.04 LTS
- CMake 3.27.0
- gcc 13.1.0
- google/benchmark v1.8.3
cf. Requirements (google/Benchmark)
Google Benchmark とは
A library to benchmark code snippets, similar to unit tests.
ベンチマークを作成するためのライブラリ。(ヘッダオンリーではない。)
C++03 以降で利用可能だが、C++11 以降とでは使い方が一部異なる。以降も C++11 以上の環境を前提として説明する。
Valgrind などのプロファイリングツールと異なり既存プログラムの内部詳細を解析することはできない。主に関数を対象として、複数の実行設定(パラメタの組み合わせ)を比較するベンチマークプログラムの作成を補助するライブラリである。
google/googletest とは近い設計スタイルで、 Benchmark の実装にも使用されているが、併用の必要性やシナジーは特にない。
導入
リポジトリに記載が無かったが、 FetchContent
を使用して導入できる。
ビルドオプションは /CMakeLists.txt (google/Benchmark) を参照した。以下では時間がかかるユニットテストを特に無効にしている。
# disable building the unit tests
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE)
include(FetchContent)
FetchContent_Declare(
gbench
URL https://github.com/google/benchmark/archive/refs/tags/v1.8.3.zip
DOWNLOAD_EXTRACT_TIMESTAMP OFF)
FetchContent_MakeAvailable(gbench)
正常にビルドできた場合、 後は以下のように executable にリンクするだけで使用できる。
target_link_libraries(${YOUR_TARGET_NAME} benchmark::benchmark)
使い方
Minimum Example
- ヘッダは
benchmark/benchmark.h
と間接的にbenchmark/export.h
をインクルードする。 - 任意の関数を直接登録できるわけではなく、
benchmark::State
オブジェクトを引数に取る関数でラップする必要がある。 -
BENCHMARK
マクロで関数をベンチマークとして登録する。同じ関数をパラメタを変えて複数回登録できるが、 後述のBenchmark::Name
でベンチマーク名を分ける必要がある。 -
BENCHMARK_MAIN
マクロはmain
関数を定義する。ユーザーでmain
関数を定義することも可能。ただし戻り値 0 を想定するため、 GoogleTest と併用できない。
#include <benchmark/benchmark.h> // namespace benchmark
void TargetFunction() {} // benchmark target
// define the benchmark function with benchmark::State argument
static void BM_TargetFunction(benchmark::State &state) {
for (auto _ : state)
TargetFunction();
}
// Register the function as a benchmark
BENCHMARK(BM_TargetFunction);
// expand to main function
BENCHMARK_MAIN();
ビルドした実行ファイルを実行すると、以下のような結果がコンソールに出力される。(出力形式は変更可能。)
2024-05-01T10:00:00+00:00
Running ./MinimumExample
Run on (12 X 2932 MHz CPU s)
CPU Caches:
L1 Data 48 KiB (x6)
L1 Instruction 32 KiB (x6)
L2 Unified 512 KiB (x6)
L3 Unified 12288 KiB (x1)
Load Average: 0.07, 0.06, 0.07
----------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------
BM_SomeFunction 0.929 ns 0.929 ns 737031406
オプション機能
主要な機能は User Guide か /docs フォルダ下の他ドキュメントに記載がある。
ここでは特に、ベンチマークにパラメタを渡す方法と、実行オプションについて紹介する。
Passing parameters to Benchmark
BENCHMARK
マクロの戻り値で渡される benchmark::Benchmark
オブジェクトポインタからメソッドを呼び出すことで、ベンチマークごとに様々なパラメタを設定できる。
後述の実行オプションと重複する機能もある。重複して設定された場合、基本的に Benchmark
で設定された値が優先される。
Usage
BENCHMARK
マクロの戻り値にメソッドチェーンを追加していく。
意味的に矛盾がない限り、メソッドは任意の順で呼び出せる。
static void BM_DenseRange(benchmark::State &state) {
const auto array_size = static_cast<uint64_t>(state.range(0));
const auto elem_default = static_cast<int32_t>(state.range(0));
for (auto _ : state) {
std::vector<int32_t> v(array_size, elem_default);
auto data = v.data();
benchmark::DoNotOptimize(data);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_DenseRange)
->Name("benchmark/DenseRange")
->DenseRange(0, 1024, 128); // [ 0, 128, 256, ... , 1024 ]
2024-05-01T10:00:00+00:00
Running ./BasicExample
Run on (12 X 2932 MHz CPU s)
CPU Caches:
L1 Data 48 KiB (x6)
L1 Instruction 32 KiB (x6)
L2 Unified 512 KiB (x6)
L3 Unified 12288 KiB (x1)
Load Average: 0.07, 0.06, 0.07
--------------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------------
benchmark/DenseRange/0 0.233 ns 0.233 ns 1000000000
benchmark/DenseRange/128 34.2 ns 34.2 ns 20892872
benchmark/DenseRange/256 52.4 ns 52.4 ns 13404492
benchmark/DenseRange/384 73.2 ns 73.2 ns 9362482
benchmark/DenseRange/512 89.4 ns 89.4 ns 7761504
benchmark/DenseRange/640 113 ns 113 ns 6481608
benchmark/DenseRange/768 123 ns 123 ns 5699625
benchmark/DenseRange/896 139 ns 139 ns 4988107
benchmark/DenseRange/1024 157 ns 157 ns 4245010
メソッド一覧
- "Arg", "Range" を含むメソッドは、ベンチマークにパラメタを渡すためのもの。
- その他 Setup/Teardown や、測定時間、スレッド関係の機能などがある。
class BENCHMARK_EXPORT Benchmark {
/* ... */
Benchmark* Name(const std::string& name);
Benchmark* Arg(int64_t x);
Benchmark* Unit(TimeUnit unit);
Benchmark* Range(int64_t start, int64_t limit);
Benchmark* DenseRange(int64_t start, int64_t limit, int step = 1);
Benchmark* Args(const std::vector<int64_t>& args);
Benchmark* ArgPair(int64_t x, int64_t y) /* ... */;
Benchmark* Ranges(const std::vector<std::pair<int64_t, int64_t> >& ranges);
Benchmark* ArgsProduct(const std::vector<std::vector<int64_t> >& arglists);
Benchmark* ArgName(const std::string& name);
Benchmark* ArgNames(const std::vector<std::string>& names);
Benchmark* Setup(void (*setup)(const benchmark::State&));
Benchmark* Teardown(void (*teardown)(const benchmark::State&));
Benchmark* Apply(void (*func)(Benchmark* benchmark));
Benchmark* RangeMultiplier(int multiplier);
Benchmark* MinTime(double t);
Benchmark* MinWarmUpTime(double t);
Benchmark* Iterations(IterationCount n);
Benchmark* Repetitions(int n);
Benchmark* DisplayAggregatesOnly(bool value = true);
Benchmark* MeasureProcessCPUTime();
Benchmark* UseRealTime();
Benchmark* UseManualTime();
Benchmark* Complexity(BigO complexity = benchmark::oAuto);
Benchmark* Complexity(BigOFunc* complexity);
Benchmark* ComputeStatistics(const std::string& name,
StatisticsFunc* statistics,
StatisticUnit unit = kTime);
Benchmark* Threads(int t);
Benchmark* ThreadRange(int min_threads, int max_threads);
Benchmark* DenseThreadRange(int min_threads, int max_threads, int stride = 1);
Benchmark* ThreadPerCpu();
/* ... */
};
実行オプション
BENCHMARK_MAIN
マクロで展開される main
関数を持つベンチマークプログラムには実行オプションを指定できる。
実行オプションは出力テキストの形式に関するものが主で、その他のベンチマークの実行を制御するオプションとで構成される。
例えば --benchmark_out=<filename>
および --benchmark_out_format=json
オプションを指定することで、ベンチマーク結果を JSON 形式でファイルに出力できる。
実行オプション一覧
実行オプションの一覧は実行ファイルに --help
(not -h
) オプションを付けて実行することで確認できる。
benchmark [--benchmark_list_tests={true|false}]
[--benchmark_filter=<regex>]
[--benchmark_min_time=`<integer>x` OR `<float>s` ]
[--benchmark_min_warmup_time=<min_warmup_time>]
[--benchmark_repetitions=<num_repetitions>]
[--benchmark_enable_random_interleaving={true|false}]
[--benchmark_report_aggregates_only={true|false}]
[--benchmark_display_aggregates_only={true|false}]
[--benchmark_format=<console|json|csv>]
[--benchmark_out=<filename>]
[--benchmark_out_format=<json|console|csv>]
[--benchmark_color={auto|true|false}]
[--benchmark_counters_tabular={true|false}]
[--benchmark_perf_counters=<counter>,...]
[--benchmark_context=<key>=<value>,...]
[--benchmark_time_unit={ns|us|ms|s}]
[--v=<verbosity>]