Rustベンチマークはじめ
TL;DR
標準のtest crateは、Nightlyじゃないと動かない
cipepser.icon このページ自体が、criterion.rsの使い方ドキュメントになった
調査
標準のtest crate
code: rust
extern crate test;
pub fn add_two(a: i32) -> i32 {
a + 2
}
mod tests {
use super::*;
use test::Bencher;
fn bench_add_two(b: &mut Bencher) {
b.iter(|| add_two(2));
}
}
stableだと動かない。
code:sh
❯ cargo bench
Compiling bench v0.1.0
errorE0554: #![feature] may not be used on the stable release channel --> src/lib.rs:1:1
|
| ^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try rustc --explain E0554.
error: could not compile bench.
To learn more, run the command again with --verbose.
Nightlyで動く。
code:sh
❯ rustup run nightly cargo bench
Compiling bench v0.1.0
Running target/release/deps/bench-993ea1d00c657dad
running 1 tests
test tests::bench_add_two ... bench: 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured; 0 filtered out
cipepser.icon サンプルコードだと、0 nsだ。
bheisler/criterion.rsを動かす
code: フォルダ構成
❯ tree -I target
.
├── Cargo.lock
├── Cargo.toml
├── benches
│ └── my_bench.rs
└── src
└── lib.rs
Cargo.tomlの抜粋。
nameでベンチマーク対象のファイルを指定。
code: Cargo.toml
criterion = "0.3.1"
bench
name = "my_bench"
harness = false
ベンチマークしたい関数
code:lib.rs
pub fn add_two(a: i32) -> i32 {
a + 2
}
ベンチマークの実装。
たぶんbenches内に置かないとダメ。
code: my_benches.rs
extern crate criterion;
use criterion::{Criterion, black_box};
use bench::add_two;
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("my benchmark", |b| b.iter(
|| add_two(2)
));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
実行方法
code:sh
❯ cargo bench
(中略)
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) low mild
もう一回実行すると前回との差分も出してくれる。
code:sh
❯ cargo bench
Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild
🤔performanceが上がってしまったな?
black_boxについて
A function that is opaque to the optimizer, used to prevent the compiler from optimizing away computations in a benchmark.
と記載されている。コンパイラに最適化させないようにできる。
上記add_twoでblack_boxを付与してみる。
code:sh
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("my benchmark", |b| b.iter(
|| add_two(black_box(2))
));
}
実行結果(一番最初に実行した値に近い)
code:sh
❯ cargo bench
(中略)
Performance has regressed.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
もう一度実行
code:sh
❯ cargo bench
(中略)
Gnuplot not found, using plotters backend
No change in performance detected.
Found 3 outliers among 100 measurements (3.00%)
2 (2.00%) high mild
1 (1.00%) high severe
値の見方
code:sh(再掲)
上記のように3つ数字が並んでいるので、それぞれの意味を見る。
./target/criterion/my benchmark/report/index.htmlが生成されているので開く。
https://gyazo.com/d4907fa08a55a7d9c0d91893cfa6be38
CLIで実行した際に表示される値は、slopeの下界、推定量、上界であることに注意する。
ちなみにt検定もしてくれる。
https://gyazo.com/7ac29d10fdaa002de8f01a56fde9188b
複数の関数を同時にベンチマークする
同一ファイル内
mul_twoを追加する。
code:lib.rs
pub fn add_two(a: i32) -> i32 { a + 2 }
pub fn mul_two(a: i32) -> i32 { a * 2 }
code:my_bench.rs
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("add two", |b| b.iter(
|| add_two(black_box(2))
));
c.bench_function("mul two", |b| b.iter(
|| mul_two(black_box(2))
));
}
実行結果
cipepser.icon +と*って速度変わらないんだ(余談)
code:sh
❯ cargo bench
(中略)
Found 13 outliers among 100 measurements (13.00%)
1 (1.00%) low mild
6 (6.00%) high mild
6 (6.00%) high severe
Found 13 outliers among 100 measurements (13.00%)
2 (2.00%) low severe
3 (3.00%) low mild
8 (8.00%) high mild
./target/criterion/report/index.htmlも複数出てくる。
https://gyazo.com/8af6d1d74619d07f29a2cb623d21f240
別ファイル
code:Cargo.toml
bench
name = "my_bench"
harness = false
bench
name = "my_bench_other"
harness = false
code:sh
❯ tree -I "target"
.
├── Cargo.lock
├── Cargo.toml
├── benches
│ ├── my_bench.rs
│ └── my_bench_other.rs
└── src
└── lib.rs
2 directories, 5 files
code:sh
❯ cargo bench
(中略)
No change in performance detected.
Found 8 outliers among 100 measurements (8.00%)
1 (1.00%) low severe
4 (4.00%) high mild
3 (3.00%) high severe
No change in performance detected.
Found 9 outliers among 100 measurements (9.00%)
1 (1.00%) low severe
3 (3.00%) low mild
2 (2.00%) high mild
3 (3.00%) high severe
(中略)
No change in performance detected.
Found 8 outliers among 100 measurements (8.00%)
1 (1.00%) low mild
4 (4.00%) high mild
3 (3.00%) high severe
bench_with_inputの使い方
code:Cargo.toml(抜粋)
bench
name = "my_bench_with_input"
harness = false
name(String)を関数に渡し、その文字数(len())をadd_twoで計算させる例。
code:rust
extern crate criterion;
use criterion::BenchmarkId;
use criterion::{Criterion, black_box};
use bench::add_two;
fn criterion_benchmark(c: &mut Criterion) {
let name = "Alice".to_string();
c.bench_with_input(
BenchmarkId::new("with input", &name),
&name,
|b, n| b.iter(
|| add_two(black_box(n.len() as i32))
));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
実行結果
code:sh
❯ cargo bench
(中略)
BenchmarkId::new("with input", &name)の第2引数で渡したものが、結果の出力で使われる。
上記の例だと with input/Alice となっている部分。
BenchmarkId::new("with input", "Bob"),とした場合
(let name = "Alice".to_string();はそのまま)
code:sh
❯ cargo bench
(中略)
上記のようにBenchmarkIdを使いたい場合は便利そうだけど、以下のように自由変数的にベンチマークを測定することも可能。
code:rust
extern crate criterion;
use criterion::Criterion;
use bench::add_two;
fn criterion_benchmark(c: &mut Criterion) {
let name = "Alice".to_string();
c.bench_function("with input", |b| b.iter(
|| add_two(black_box(name.len() as i32))
));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
code:sh
❯ cargo bench
(中略)
Found 4 outliers among 100 measurements (4.00%)
3 (3.00%) high mild
1 (1.00%) high severe
ドキュメントには以下のように記載があり、自身でblack_boxに包まなくていい。 This is convenient in that it automatically passes the input through a black_box so that you don't need to call that directly.
benchmark_groupの使い方
関連ベンチマークのグルーピングが可能
configをカスタマイズする場合に用いる
final reportにもまとめて出力される
code:rust
extern crate criterion;
use criterion::Criterion;
use bench::{add_two, mul_two};
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("group-example");
group.bench_function("Bench add_two", |b| b.iter(|| add_two(1)));
group.bench_function("Bench mul_two", |b| b.iter(|| mul_two(1)));
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
結果
code:sh
❯ cargo bench
(中略)
group-example/Bench add_two
Found 5 outliers among 100 measurements (5.00%)
3 (3.00%) high mild
2 (2.00%) high severe
group-example/Bench mul_two
Found 6 outliers among 100 measurements (6.00%)
4 (4.00%) high mild
2 (2.00%) high severe
html出力したものも並べてみることができる。
本例でいうとmul_twoがadd_twoに比べ遅いのが一目瞭然。
(どちらもnsオーダーだが)
https://gyazo.com/1e876481dfd654e0a0650e23cac0546b
https://gyazo.com/001c5ee07fab0f1cd2f81bb77294367a
References