LoginSignup
2
0

google/benchmark の導入から簡単な使い方まで

Last updated at Posted at 2024-05-09

目的

google/Benchmark の導入、簡単な使い方を説明する。
概ねリポジトリに記載のある内容だが、 README.md も十分に親切というわけではないため、理解を深めるためにもこちらで自分なりにまとめる。

参考文献

リポジトリに含まれるドキュメントを参照すれば十分利用できる。またヘッダファイルにも多くのコメントが含まれている。

開発環境

以下の環境で動作確認を行った。

  • 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) を参照した。以下では時間がかかるユニットテストを特に無効にしている。

FetchContent
# 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 にリンクするだけで使用できる。

linking benchmark
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 と併用できない。
minimum_example
#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 マクロの戻り値にメソッドチェーンを追加していく。
意味的に矛盾がない限り、メソッドは任意の順で呼び出せる。

UserGuide記載の例を一部修正
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 や、測定時間、スレッド関係の機能などがある。
benchmark.hよりメソッド抜粋

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>]
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0