0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

hyperfine (ベンチマーク測定コマンド)の紹介

Posted at

この記事を書くにあたって

アプリケーション、パッケージやコマンドラインの検証等でベンチマークを測定する際、
shell組み込みのtimeコマンドを使うことが自分は多い。なお /usr/bin/time のGNU time を使うことで実行時間だけでなくCPUや平均・最大使用メモリも測定できるが、今回はその話題に触れない。

私が感じるhyperfineの魅力は、ベンチマークの比較ができること、warmupが指定できることである。それらは以下で解説する。

hyperfine とは

Rust製のベンチマークコマンドである。installは cargobrew などが簡単であろう。
See Installation

主な使い方

timeコマンドとは異なり、ベンチマークの実行対象となるコマンドは '文字リテラル' で入れる必要がある。

# ❌
hyperfine sleep 0.3

# ✅
hyperfine 'sleep 0.3'

デフォルトの実行回数は10回だが、--runs (or -r) で回数を指定できる。

hyperfine --runs 5 'sleep 0.3'

コマンドの比較

大抵のベンチマークを取る目的は、コードの改善などの比較のためにおこなわれる。hyperfine は二つコマンドを取ると、それぞれの結果と比較の出力する。以下は全く面白みのないベンチマークの比較を記載する。

hyperfine -r 5 'sleep 0.2' 'sleep 0.3'
Benchmark 1: sleep 0.2
  Time (mean ± σ):     202.4 ms ±   0.1 ms    [User: 2.0 ms, System: 0.5 ms]
  Range (min  max):   202.2 ms  202.6 ms    5 runs

Benchmark 2: sleep 0.3
  Time (mean ± σ):     302.9 ms ±   0.3 ms    [User: 2.6 ms, System: 0.6 ms]
  Range (min  max):   302.6 ms  303.4 ms    5 runs

Summary
  sleep 0.2 ran
    1.50 ± 0.00 times faster than sleep 0.3

なお比較は2個以上も可能である。

 hyperfine -r 3 'sleep 0.1' 'sleep 0.15' 'sleep 0.2' 'sleep 0.25'
Benchmark 1: sleep 0.1
  Time (mean ± σ):     103.0 ms ±   1.1 ms    [User: 2.4 ms, System: 0.8 ms]
  Range (min  max):   101.7 ms  103.9 ms    3 runs

Benchmark 2: sleep 0.15
  Time (mean ± σ):     153.6 ms ±   1.1 ms    [User: 2.4 ms, System: 1.3 ms]
  Range (min  max):   152.8 ms  154.8 ms    3 runs

Benchmark 3: sleep 0.2
  Time (mean ± σ):     203.8 ms ±   1.2 ms    [User: 3.0 ms, System: 0.9 ms]
  Range (min  max):   202.4 ms  204.7 ms    3 runs

Benchmark 4: sleep 0.25
  Time (mean ± σ):     252.4 ms ±   0.8 ms    [User: 2.6 ms, System: 0.0 ms]
  Range (min  max):   251.6 ms  253.2 ms    3 runs

Summary
  sleep 0.1 ran
    1.49 ± 0.02 times faster than sleep 0.15
    1.98 ± 0.02 times faster than sleep 0.2
    2.45 ± 0.03 times faster than sleep 0.25

warmup

さて、hyperfine が time コマンドより便利であると感じたのは、 --warmup (or -w) コマンドである。ディスクからの読み込みなど I/O の影響がある場合は warmup により(CPUなど部分的に)cacheされるため、I/O 部分の影響を(厳密ではないが)排除できる。

例えば DuckDBの read_parquet にてデータの読み込みについて、--warmup指定無しの場合(おそらく最初の1回目が) Range max の値となり、平均値はそれに引きずられていると思われる。

hyperfine --runs 5 $'duckdb -c "FROM read_parquet(\'lineitem.parquet\');"'
# Benchmark 1: duckdb -c "FROM read_parquet('lineitem.parquet');"
#   Time (mean ± σ):     530.9 ms ±  72.1 ms    [User: 5626.9 ms, System: 1379.5 ms]
#   Range (min … max):   486.7 ms … 658.2 ms    5 runs

-w 1 と warmup を1回入れて実行すると、Rangeも安定的であるので、ベンチマーク測定1回目からcacheが効いていると推測される。ことが確認できる。

hyperfine -w1 --runs 5 $'duckdb -c "FROM read_parquet(\'lineitem.parquet\');"'
# Benchmark 1: duckdb -c "FROM read_parquet('lineitem.parquet');"
#   Time (mean ± σ):     488.5 ms ±  10.7 ms    [User: 5478.7 ms, System: 1205.1 ms]
#   Range (min … max):   478.5 ms … 504.9 ms    5 runs

逆にI/Oの影響を測定したい、すなわち cold start をする場合は --prepare というオプションがある。公式での例を参照されたい

出力フォーマット

以下の指定が可能である

  • --export-markdown $FILENAME
  • --export-json $FILENAME
  • --export-csv $FILENAME

出力フォーマットは以下のようになる。json形式が個別の測定値まで持ち一番情報量が多い。

結果を可視化するには json形式で出力し、公式の python script から plot_whisker.py 等をローカルにダウンロードして実行すればよい。ここでも uv の威力が発揮する。

uv run plot_whisker.py bench-out.json

以下 --export-* の実行結果

markdown

hyperfine -w1 --runs 5 $'duckdb -c "FROM read_parquet(\'lineitem.parquet\');"' --export-markdown /dev/stdout
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `duckdb -c "FROM read_parquet('lineitem.parquet');"` | 518.3 ± 18.8 | 494.4 | 541.8 | 1.00 |

json

hyperfine -w1 --runs 5 $'duckdb -c "FROM read_parquet(\'lineitem.parquet\');"' --export-json /dev/stdout
{
  "results": [
    {
      "command": "duckdb -c \"FROM read_parquet('lineitem.parquet');\"",
      "mean": 0.52732588448,
      "stddev": 0.04589734886338472,
      "median": 0.51568904428,
      "user": 5.718178199999999,
      "system": 1.4224087800000003,
      "min": 0.49069895828000004,
      "max": 0.60658191028,
      "times": [
        0.51568904428,
        0.50214623628,
        0.60658191028,
        0.52151327328,
        0.49069895828000004
      ],
      "exit_codes": [
        0,
        0,
        0,
        0,
        0
      ]
    }
  ]
}

csv

hyperfine -w1 --runs 5 $'duckdb -c "FROM read_parquet(\'lineitem.parquet\');"' --export-csv /dev/stdout
command,mean,stddev,median,user,system,min,max
"duckdb -c ""FROM read_parquet('lineitem.parquet');""",0.5254087186600002,0.03960067567439999,0.51560465546,5.860614059999999,1.2543148,0.49691412846000005,0.59473722746

以上

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?