LoginSignup
4
3

C/C++パフォーマンスチューニング Google Benchmarkに入門しよう!

Last updated at Posted at 2022-02-05

はじめに

C++のcontainerライブラリの自作に取り組んでいます。
ライブラリを自作したら、スタンダードライブラリとの比較をしたいと思って、いろいろ調べていたら、Google Benchmarkというツールに出会いました。

Google Testと同様に、簡単に書くことが出来て、多機能です。

インストール

こちらのREADMEを見れば丁寧に書いてあるので、そのままやるだけなのですが、一応こちらでも書いておきます。

cmakeが入っていない人はbrewで入れましょう。

brew install cmake

以下の手順を実行します。

# クローンします
git clone https://github.com/google/benchmark.git

# ライブラリのルートディレクトリに移動します
cd benchmark

# ビルドディレクトリを作成して、ビルド出力を配置します。
cmake -E make_directory "build"

# 依存関係を持つシステムファイルを作成し、依存関係をダウンロードします。
cmake -E chdir "build" cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release ../

# ライブラリをbuildします
cmake --build "build" --config Release

実行方法

インストールができたら、実行ファイルを書きましょう。

#include <benchmark/benchmark.h>

static void BM_StringCreation(benchmark::State& state) {
  for (auto _ : state) {
    std::string empty_string;
  }
}
// Register the function as a benchmark
BENCHMARK(BM_StringCreation);

// Define another benchmark
static void BM_StringCopy(benchmark::State& state) {
  std::string x = "hello";
  for (auto _ : state) {
    std::string copy(x);
  }
}
BENCHMARK(BM_StringCopy);

BENCHMARK_MAIN();

コンパイル

書いたファイルを巻き込んでコンパイルします。

g++ mybenchmark.cc -std=c++11 -isystem benchmark/include \
  -Lbenchmark/build/src -lbenchmark -lpthread -o mybenchmark

実行

$ ./mybenchmark
2022-02-05T21:36:18+09:00
Running ./benchmark
Run on (8 X 2200 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x4)
  L1 Instruction 32 KiB (x4)
  L2 Unified 256 KiB (x4)
  L3 Unified 6144 KiB (x1)
Load Average: 2.75, 3.06, 2.87
------------------------------------------------------------
Benchmark                  Time             CPU   Iterations
------------------------------------------------------------
BM_StringCreation       34.5 ns         34.1 ns     20878446
BM_StringCopy           8.39 ns         8.19 ns     84670932

Benchmarkが出ました!

Syntaxの説明

今、自分が使っているコードで説明します。

#include <benchmark/benchmark.h>
#include <vector>
#include "vector.hpp"

static void BM_FtVector(benchmark::State& state) {
  // Benchmarkでは測らない前処理をここで書いておきます。
  ft::vector<int> v;

  // このfor文の中にBenchmarkを測りたい処理を書きます。
  for (auto _ : state) {
    for (int i = 0; i < state.range(0); ++i) {
      v.push_back(i);
    }
  }
}
// Benchmarkを実行するには、このマクロを書きます。
// Range(1, 1 << 22)で、state.range(0)に段階的に渡す数を指定できます。
BENCHMARK(BM_FtVector)->Range(1, 1 << 22);

static void BM_StdVector(benchmark::State& state) {
  std::vector<int> v;
  for (auto _ : state) {
    for (int i = 0; i < state.range(0); ++i) {
      v.push_back(i);
    }
  }
}
BENCHMARK(BM_StdVector)->Range(1, 1 << 22);

// main関数
BENCHMARK_MAIN();

Benchmarkの見方

Benchmarkの出力結果は実行環境情報と、実行結果の詳細の2つが出力されます。

実行環境情報

2022-02-05T21:42:00+09:00
Running ./benchmark
Run on (8 X 2200 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x4)
  L1 Instruction 32 KiB (x4)
  L2 Unified 256 KiB (x4)
  L3 Unified 6144 KiB (x1)
Load Average: 2.62, 3.19, 3.03

実行結果

  • Benchmark名/渡した引数
  • 時間
  • CPU時間
  • 反復回数

反復回数が記載されているので、どれくらいの平均が出されているのか、ひと目で分かることもGoogle Benchmarkのいいところです。

---------------------------------------------------------------
Benchmark                     Time             CPU   Iterations
---------------------------------------------------------------
BM_FtVector/1              71.0 ns         65.7 ns     11932361
BM_FtVector/8               416 ns          403 ns      1784617
BM_FtVector/64             2881 ns         2856 ns       238887
BM_FtVector/512           22778 ns        22487 ns        30598
BM_FtVector/4096         181550 ns       180412 ns         3816
BM_FtVector/32768       1444000 ns      1434107 ns          460
BM_FtVector/262144     12002002 ns     11827379 ns           58
BM_FtVector/2097152    91833606 ns     91457000 ns            6
BM_FtVector/4194304   183728092 ns    182849500 ns            4
BM_StdVector/1             70.6 ns         70.1 ns     11003867
BM_StdVector/8              460 ns          457 ns      1598152
BM_StdVector/64            3484 ns         3468 ns       201855
BM_StdVector/512          28056 ns        27935 ns        23835
BM_StdVector/4096        219552 ns       218309 ns         3385
BM_StdVector/32768      1769702 ns      1760995 ns          400
BM_StdVector/262144    14391678 ns     14313447 ns           47
BM_StdVector/2097152  110284984 ns    109849714 ns            7
BM_StdVector/4194304  227442098 ns    226656333 ns            3

まとめ

試していませんが、他にも様々な機能があるようです。

  • スループットの表示
  • メモリのBenchmark
  • 出力形式でcsv, jsonを指定できる
  • スレッド数の指定
  • 最適化の抑制
  • 環境設定の上書き

よく仲間内で、俺のコードが最強だ!という話で盛り上がっていますが、計測が適当な部分があったので、Google Benchmarkで実行速度比較を出して、より良いコードを書けるよう勉強していこうと思います。

4
3
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
4
3