Iaiを使用してRustコードをベンチマークする方法
Everett Pompeii
ベンチマークとは?
ベンチマークとは、コードの性能をテストして、その処理速度(レイテンシ)や処理量(スループット)を確認することを指します。 この、ソフトウェア開発において見落とされがちなステップは、高速で高性能なコードを作成および維持するために重要です。 ベンチマークは開発者がコードがさまざまな作業負荷や条件下でどれだけうまく動作するかを理解するために必要な指標を提供します。 機能のリグレッションを防ぐためにユニットテストや統合テストを書くのと同様に、 パフォーマンスのリグレッションを防ぐためにもベンチマークを書くべきです。 パフォーマンスのバグもバグです!
RustでFizzBuzzを書く
ベンチマークを書くためには、ベンチマークするソースコードが必要です。 まず、非常にシンプルなプログラム、 FizzBuzz を書いてみましょう。
FizzBuzzのルールは以下の通りです:
1から100までの整数を印刷するプログラムを書く:
- 三の倍数の場合は、
Fizzを印刷します- 五の倍数の場合は、
Buzzを印刷します- 三と五の両方の倍数の場合は、
FizzBuzzを印刷します- それ以外の場合は、数字を印刷します
FizzBuzzは多くの方法で書くことができます。 なので、私のお気に入りの方法で進めることにしましょう。
fn main() { for i in 1..=100 { match (i % 3, i % 5) { (0, 0) => println!("FizzBuzz"), (0, _) => println!("Fizz"), (_, 0) => println!("Buzz"), (_, _) => println!("{i}"), } }}main関数を作成する1から100までを含む範囲で繰り返します。- 各数値に対して、
3と5の両方で余り(除算後の余り)を計算します。 - 2つの余りにパターンマッチを行います。
余りが
0の場合、その数は指定された因数の倍数です。 3と5の両方で余りが0の場合はFizzBuzzを出力します。3のみで余りが0の場合はFizzを出力します。5のみで余りが0の場合はBuzzを出力します。- それ以外の場合は、単に数字を出力します。
ステップバイステップで進める
このステップバイステップのチュートリアルを進めるには、まずRustをインストールする必要があります。
🐰 このポストのソースコードはGitHub上で利用可能です。
Rustがインストールされたら、ターミナルウィンドウを開き、次のコマンドを入力します:cargo init game
その後、新たに作成されたgameディレクトリに移動します。
game├── Cargo.toml└── src └── main.rssrcという名前のディレクトリがあり、その中にmain.rsというファイルが存在しているはずです:
fn main() { println!("Hello, world!");}先ほどのFizzBuzzの実装をその内容と置き換えてから、cargo runを実行します。
出力結果は次のようになるはずです:
$ cargo run Compiling playground v0.0.1 (/home/bencher) Finished dev [unoptimized + debuginfo] target(s) in 0.44s Running `target/debug/game`
12Fizz4BuzzFizz78FizzBuzz11Fizz1314FizzBuzz...9798FizzBuzz🐰 ブーム! コーディングインタビューを突破していますね!
新たなCargo.lockファイルが生成されているはずです:
game├── Cargo.lock├── Cargo.toml└── src └── main.rsこれ以上進む前に、マイクロベンチマークとマクロベンチマークの違いについて話すことが重要です。
マイクロベンチマークとマクロベンチマーク
ソフトウェアベンチマークには、マイクロベンチマークとマクロベンチマークの2つの主要なカテゴリーがあります。
マイクロベンチマークは、ユニットテストと同様のレベルで動作します。
例えば、単一の数値に対してFizz、Buzz、またはFizzBuzzを決定する関数のベンチマークはマイクロベンチマークになります。
一方、マクロベンチマークは、統合テストと同様のレベルで動作します。
例えば、1から100までの全ゲームをプレイするFizzBuzzの関数のベンチマークは、マクロベンチマークになります。
一般的に、可能な限り低い抽象レベルでテストすることが最善です。 ベンチマークの場合、これにより保守性が向上し、測定値のノイズを減らすことに役立ちます。 しかし、エンドツーエンドテストがシステム全体が予想通りに組み合わさるかのサニチェックに非常に役立つように、 マクロベンチマークがあると、ソフトウェアを通る重要なパスが性能を維持するために非常に役立ちます。
Rustにおけるベンチマーク
Rustでのベンチマークにおいて人気のあるオプションは、libtest bench、Criterion、そしてIaiです。
libtestはRustの組み込みユニットテストとベンチマークフレームワークです。
Rust標準ライブラリの一部であるにもかかわらず、libtest benchはまだ不安定とされており、nightlyコンパイラリリースでのみ利用可能です。
安定バージョンのRustコンパイラで動作させるためには、別のベンチマークハーネスを使用する必要があります。
しかし、どちらも積極的に開発されているわけではありません。
Rustエコシステム内で最も人気のあるベンチマークハーネスはCriterionです。
これは安定版とnightly版の両方のRustコンパイラリリースで動作し、Rustコミュニティ内で事実上の標準として定着しています。
Criterionはlibtest benchと比べてはるかに多機能です。
Criterionの実験的な代替としてIaiがありますが、これはCriterionの作成者によって開発されたものです。 しかし、壁時計時間の代わりに命令カウントを使用します:CPU命令、L1アクセス、L2アクセス、RAMアクセスです。 これにより、これらのメトリクスはラン間でほぼ同一であるべきなので、シングルショットベンチマークが可能になります。
すべてがベンチャーによってサポートされています。なぜIaiを選ぶのでしょうか? Iaiは壁掛け時間ではなく、命令数を使用します。 これは、CIでのベンチマーク、つまり連続的なベンチマーキングに理想的です。 特に共有ランナーを使用している場合、CIでの連続的なベンチマーキングにはIaiを使用することをお勧めします。 Iaiが本当に気になることの代理としか測定しないことを理解することが重要です。 1,000命令から2,000命令に増加することで、アプリケーションのレイテンシが2倍になるのでしょうか? それはもしかしたらそうかもしれませんし、そうでないかもしれません。 このため、命令数ベースのベンチマークと並行して壁掛け時間ベースのベンチマークを実行することも有用です。
🐰 Iai はここ数年 更新されていません。代わりに Gungraun を使う ことを検討してください。
Valgrindのインストール
Iaiは命令数を収集するためにValgrindというツールを使用します。 ValgrindはLinux、Solaris、FreeBSD、macOSをサポートしています。 ただし、macOSのサポートはx86_64プロセッサに限定されており、まだarm64(M1、M2など)プロセッサはサポートされていません。
Debianでの実行:sudo apt-get install valgrind
macOS(x86_64/Intelチップのみ):brew install valgrind
FizzBuzzのリファクタリング
FizzBuzzアプリケーションをテストするためには、ロジックとプログラムのmain関数を切り離す必要があります。
ベンチマークハーネスはmain関数をベンチマークできません。これを行うためには、いくつかの変更を行う必要があります。
srcの下に、新しいファイルlib.rsを作成します:
game├── Cargo.lock├── Cargo.toml└── src └── lib.rs └── main.rs以下のコードをlib.rsに追加します:
pub fn play_game(n: u32, print: bool) { let result = fizz_buzz(n); if print { println!("{result}"); }}
pub fn fizz_buzz(n: u32) -> String { match (n % 3, n % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (_, _) => n.to_string(), }}play_game: 符号なし整数nを指定してfizz_buzzを呼び出し、printがtrueの場合は結果を出力します。fizz_buzz: 符号なし整数nを受け取り、実際のFizz、Buzz、FizzBuzz、または数字のロジックを実行して結果を文字列として返します。
その後main.rsを以下のように更新します:
use game::play_game;
fn main() { for i in 1..=100 { play_game(i, true); }}game::play_game:lib.rsで作成したgameクレートからplay_gameをインポートします。main: プログラムのメインエントリーポイントで、1から100までの数字を反復処理し、各数字に対してplay_gameを呼び出し、printをtrueに設定します。
FizzBuzzをベンチマークする
コードをベンチマークするために、benchesディレクトリを作成し、ベンチマークを含むファイルplay_game.rsを追加する必要があります:
game├── Cargo.lock├── Cargo.toml└── benches └── play_game.rs└── src └── lib.rs └── main.rsplay_game.rsの中に以下のコードを追加します:
use game::play_game;
fn bench_play_game() { iai::black_box(for i in 1..=100 { play_game(i, false) });}
iai::main!(bench_play_game);gameクレートからplay_game関数をインポートします。bench_play_gameという名前の関数を作成します。- コンパイラがコードを最適化するのを防ぐために、マクロベンチマークを”black box”の中で実行します。
1から100まで(含む)を反復します。- 各数字に対して、
play_gameを呼び出し、printを偽に設定します。
次に、ベンチマークを実行するためにgameクレートを設定する必要があります。
Cargo.tomlファイルの_下部_に以下を追加します:
[dev-dependencies]iai = "0.1"
[[bench]]name = "play_game"harness = falseiai: パフォーマンステストにのみ使用するため、iaiを開発依存関係として追加します。bench:play_gameをベンチマークとして登録し、ハーネスをfalseに設定します。なぜなら、私たちはIaiをベンチマークハーネスとして使用する予定だからです。
これでコードのベンチマークが可能になりました。 cargo benchを実行します:
$ cargo bench Compiling iai v0.1.1 Compiling game v0.1.0 (/home/bencher) Finished bench [optimized] target(s) in 2.55s Running unittests src/lib.rs (target/release/deps/game-9b1b504669ca4b29)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/release/deps/game-8d61ca5a97299729)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running benches/play_game.rs (target/release/deps/play_game-6896309faf45cd96)bench_play_game Instructions: 34370 L1 Accesses: 50373 L2 Accesses: 9 RAM Accesses: 35 Estimated Cycles: 51643🐰 レタスのビートをターンアップ! 我々は我々の最初のベンチマーク測定値を得ました!
ついに、我々は疲れ果てた開発者の頭を休めることができます… 冗談です、ユーザーは新機能を望んでいます!
RustでFizzBuzzFibonacciを書く
私たちの主要業績指標(KPI)が下降しているため、製品マネージャー(PM)は新機能の追加を希望しています。多くのブレインストーミングとユーザーインタビューの結果、単なるFizzBuzzだけでは足りないと判断されました。今日の子供たちは新しいゲーム、FizzBuzzFibonacciを求めています。
FizzBuzzFibonacciの規則は以下の通りです:
1から100までの整数を印刷するプログラムを書く:
- 3の倍数には
Fizzを印刷- 5の倍数には
Buzzを印刷- 三と五の倍数には
FizzBuzzを印刷- フィボナッチ数列の一部である数には、
Fibonacciだけを印刷- それ以外のすべてには、その数値を印刷
フィボナッチ数列は、それぞれの数が前の二つの数の和である数列です。
例えば、0と1から始めると、フィボナッチ数列の次の数は1になります。
そして、それに続く数は:2, 3, 5, 8 と続きます。
フィボナッチ数列の一部である数はフィボナッチ数として知られています。なので、フィボナッチ数を検出する関数を書く必要があります。
フィボナッチ数列を書く方法はたくさんありますし、同様にフィボナッチ数を検出する方法もたくさんあります。 だから私のお気に入りを選びます:
fn is_fibonacci_number(n: u32) -> bool { for i in 0..=n { let (mut previous, mut current) = (0, 1); while current < i { let next = previous + current; previous = current; current = next; } if current == n { return true; } } false}is_fibonacci_numberという名前の関数を作成し、符号なし整数を引数に取り、ブールを返します。0から我々の与えられた数nの間で全ての数値に対して反復します。- フィボナッチ数列を
0および1から開始し、それぞれをpreviousとcurrentの両方の数値とし初期化します。 currentの数値が現在のiの反復より少ない間反復します。previousとcurrentの数値を加えてnextの数値を取得します。previousの数値をcurrentの数値にアップデートします。currentの数値をnextの数値にアップデートします。- 一度
currentが与えられた数値nと等しいかそれ以上になったら、ループを終了します。 currentの数値が与えられた数値nと等しいかどうか確認し、そうであればtrueを返します。- それ以外の場合は、
falseを返します。
次に fizz_buzz 関数を更新する必要があります:
pub fn fizz_buzz_fibonacci(n: u32) -> String { if is_fibonacci_number(n) { "Fibonacci".to_string() } else { match (n % 3, n % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (_, _) => n.to_string(), } }}fizz_buzz関数の名前をfizz_buzz_fibonacciに変更して、より説明的にします。- 弊社の
is_fibonacci_numberヘルパー関数を呼び出します。 is_fibonacci_numberから得た結果がtrueの場合はFibonacciを返します。is_fibonacci_numberの結果がfalseの場合は、同じFizz、Buzz、FizzBuzz、または数値のロジックを実行し、結果を返します。
fizz_buzz を fizz_buzz_fibonacci に変更するため、 play_game 関数も更新する必要があります:
pub fn play_game(n: u32, print: bool) { let result = fizz_buzz_fibonacci(n); if print { println!("{result}"); }}私たちの main と bench_play_game の両関数は全く同じままで構いません。
FizzBuzzFibonacciのベンチマーク
これで、私たちのベンチマークを再度実行することができます:
$ cargo bench Compiling game v0.1.0 (/home/bencher) Finished bench [optimized] target(s) in 2.20s Running unittests src/lib.rs (target/release/deps/game-9b1b504669ca4b29)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/release/deps/game-8d61ca5a97299729)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running benches/play_game.rs (target/release/deps/play_game-6896309faf45cd96)bench_play_game Instructions: 304598 (+786.2322%) L1 Accesses: 320024 (+535.3086%) L2 Accesses: 8 (-11.11111%) RAM Accesses: 42 (+20.00000%) Estimated Cycles: 321534 (+522.6091%)おお、それは素晴らしい!Iaiは、私たちのFizzBuzzとFizzBuzzFibonacciゲームの推定サイクルの差が +522.6091%であると言っています。
あなたの数字は私のものと少し異なるでしょう。
ただし、2つのゲーム間の違いは、おそらく5xの範囲にあります。
私はそれが良いと思う! 特にゲームに_Fibonacci_という派手な機能を追加することにより。子供たちはそれを愛するでしょう!
RustにてFizzBuzzFibonacciを展開
我々のゲームは大ヒットです!子供たちは確かにFizzBuzzFibonacciを遊ぶのが大好きです。
それほどに、経営陣から続編を求める声が聞こえてきました。
しかし、これは現代の世界、我々は一度きりの購入ではなく、年間定期収入(ARR)が必要です!
我々のゲームの新たなビジョンは、それがオープンエンドであり、1から100(包含)の間に生息するのではなく、新たなフロンティアへと向かうことです。
Open World FizzBuzzFibonacciのルールは次のとおりです:
以下のように、任意の 正の整数を受け取って印刷するプログラムを書きます:
- 3の倍数の場合、
Fizzを出力する- 5の倍数の場合、
Buzzを出力する- 3と5の両方の倍数の場合、
FizzBuzzを出力する- フィボナッチ数列の一部である数値は、
Fibonacciのみを出力する- それ以外の場合は、数値を出力する
我々のゲームが任意の数値で動作できるようにするため、コマンドライン引数を受け取る必要があります。
main関数を以下のように更新してください:
fn main() { let args: Vec<String> = std::env::args().collect(); let i = args .get(1) .map(|s| s.parse::<u32>()) .unwrap_or(Ok(15)) .unwrap_or(15); play_game(i, true);}- コマンドラインから我々のゲームに渡されたすべての引数(
args)を収集します。 - 我々のゲームに渡された最初の引数を取得し、それを符号なし整数
iとして解析します。 - 解析に失敗した場合、または引数が渡されない場合は、入力として
15を用いて我々のゲームをデフォルトで遊びます。 - 最後に、新たに解析した符号なし整数
iで我々のゲームを遊びます。
これで我々のゲームは何の数でも遊べます!
我々のゲームに引数を渡すためにcargo runの後に--を使用してください:
$ cargo run -- 9 Compiling playground v0.0.1 (/home/bencher) Finished dev [unoptimized + debuginfo] target(s) in 0.44s Running `target/debug/game 9`Fizz$ cargo run -- 10 Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `target/debug/game 10`Buzz$ cargo run -- 13 Finished dev [unoptimized + debuginfo] target(s) in 0.04s Running `target/debug/game 13`Fibonacciそして、もし我々が数字を省略したり、無効な数字が提供されたりすると:
$ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `target/debug/game`FizzBuzz$ cargo run -- bad Finished dev [unoptimized + debuginfo] target(s) in 0.05s Running `target/debug/game bad`FizzBuzzうわー、それは手厚いテストだった!CIがパスします。我々の上司たちは大喜びです。 それでは、出荷しましょう! 🚀
終わり


🐰 … あなたのキャリアの終わりかもしれない?
冗談じゃない!全てが炎上しています!🔥
最初は全てうまく行っているように見えました。 しかし、土曜日の午前2時07分に僕のページャーが鳴った:
📟 あなたのゲームが炎上しています!🔥
ベッドから飛び起き、何が起こっているのかを理解しようとしました。 ログを検索しようと試みましたが、何もかもがクラッシュし続けていて困難でした。 最終的に、問題を見つけました。その子供たち! 彼らは私たちのゲームが大好きで、最大百万までプレイしていました! ひらめきの一瞬で、新たに2つのベンチマークを追加しました:
fn bench_play_game_100() { iai::black_box(play_game(100, false));}
fn bench_play_game_1_000_000() { iai::black_box(play_game(1_000_000, false));}- 数字百でゲームをするミクロベンチマーク
bench_play_game_100 - 数字100万でゲームをするミクロベンチマーク
bench_play_game_1_000_000
私がそれを実行したとき、私はこれを得ました:
$ cargo bench Compiling game v0.1.0 (/home/bencher) Finished bench [optimized] target(s) in 1.92s Running unittests src/lib.rs (target/release/deps/game-9b1b504669ca4b29)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/release/deps/game-8d61ca5a97299729)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running benches/play_game.rs (target/release/deps/play_game-6896309faf45cd96)bench_play_game Instructions: 304598 (No change) L1 Accesses: 320025 (+0.000312%) L2 Accesses: 7 (-12.50000%) RAM Accesses: 42 (No change) Estimated Cycles: 321530 (-0.001244%)
bench_play_game_100 Instructions: 6194 L1 Accesses: 6290 L2 Accesses: 2 RAM Accesses: 11 Estimated Cycles: 6685それを待って…それを待って…
bench_play_game_1_000_000 Instructions: 155108715 L1 Accesses: 155108811 L2 Accesses: 2 RAM Accesses: 11 Estimated Cycles: 155109206何! 6,685推定サイクル x 1,000は 6,685,000推定サイクルであるべきであり、 155,109,206推定サイクルではない🤯
フィボナッチシーケンスのコードが機能的には正しいにもかかわらず、何かしらのパフォーマンスのバグがあるはずです。
RustでFizzBuzzFibonacciを修正する
もう一度 is_fibonacci_number関数を見てみましょう:
fn is_fibonacci_number(n: u32) -> bool { for i in 0..=n { let (mut previous, mut current) = (0, 1); while current < i { let next = previous + current; previous = current; current = next; } if current == n { return true; } } false}パフォーマンスを考えると、不要な余分なループがあることに気づきます。
for i in 0..=n {} ループを完全に取り除き、
与えられた数値(n)と current の値を単に比較するだけで良いのです🤦
fn is_fibonacci_number(n: u32) -> bool { let (mut previous, mut current) = (0, 1); while current < n { let next = previous + current; previous = current; current = next; } current == n}- あなたの
is_fibonacci_number関数を更新します。 - フィボナッチ数列を
0と1から始めるpreviousとcurrentの数で初期化します。 - 与えられた数
nよりもcurrent数が小さい間、繰り返します。 previousとcurrentの数を足してnextの数を得ます。previousの数をcurrentの数に更新します。currentの数をnextの数に更新します。currentが与えられた数nよりも大きくなれば、ループを退出します。currentの数が与えられた数nと等しいかどうかを確認し、その結果を返します。
それでは、これらのベンチマークを再実行して、どのようになったか見てみましょう:
$ cargo bench Compiling game v0.1.0 (/home/bencher) Finished bench [optimized] target(s) in 4.22s Running unittests src/lib.rs (target/release/deps/game-9b1b504669ca4b29)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/release/deps/game-8d61ca5a97299729)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running benches/play_game.rs (target/release/deps/play_game-6896309faf45cd96)bench_play_game Instructions: 38313 (-87.42178%) L1 Accesses: 53739 (-83.20787%) L2 Accesses: 7 (No change) RAM Accesses: 43 (+2.380952%) Estimated Cycles: 55279 (-82.80751%)
bench_play_game_100 Instructions: 295 (-95.23733%) L1 Accesses: 389 (-93.81558%) L2 Accesses: 2 (No change) RAM Accesses: 13 (+18.18182%) Estimated Cycles: 854 (-87.22513%)
bench_play_game_1_000_000 Instructions: 391 (-99.99975%) L1 Accesses: 485 (-99.99969%) L2 Accesses: 2 (No change) RAM Accesses: 13 (+18.18182%) Estimated Cycles: 950 (-99.99939%)おお、わあ! 私たちのbench_play_gameベンチマークがオリジナルのFizzBuzzのところに戻ってきました。
そのスコアが正確に何だったか思い出せたら良かったのですが。それは3週間前の事です。
私のターミナルの履歴はそれほど遡らない。
そしてIaiは最新の結果とのみ比較します。
しかし、それは近いと思います!
bench_play_game_100のベンチマークはほぼ10倍、 -87.22513%減りました。
そしてbench_play_game_1_000_000のベンチマークは10,000倍以上減りました! 155,109,206推定サイクルから950推定推定サイクルに!
それは -99.99939% です!
🐰 まあ、少なくともこのパフォーマンスバグを製品版に出す前に見つけられた、ね… あ、そうだ。忘れてた…
CIでパフォーマンスの後退を捕捉する
私のちょっとしたパフォーマンスのバグが原因で我々のゲームが大量の否定的なレビューを受けたことに、エクゼクティブたちは不満を持っていました。 彼らは再びそれを起こさないようにと言い、どうすれば良いのか尋ねると、ただ再びやらないようにと言われるだけでした。 どうすればそれを管理することができるのでしょうか‽
幸運なことに、Bencherという素晴らしいオープンソースツールを見つけました。 超大盤振る舞いの無料枠があるので、私の個人的なプロジェクトではBencherクラウドをただ使うことができます。 そして、仕事では全てが私たちのプライベートクラウド内にある必要があるので、Bencher Self-Hostedを使い始めました。
Bencherには組み込みのアダプターがあり、 そのためCIに簡単に統合することができます。クイックスタートガイドをフォローした後、 私は私のベンチマークを実行し、それらをBencherで追跡することができます。
$ bencher run --project game "cargo bench" Finished bench [optimized] target(s) in 0.18s Running unittests src/lib.rs (target/release/deps/game-9b1b504669ca4b29)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/release/deps/game-8d61ca5a97299729)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running benches/play_game.rs (target/release/deps/play_game-6896309faf45cd96)bench_play_game Instructions: 38331 (+0.046981%) L1 Accesses: 53765 (+0.048382%) L2 Accesses: 6 (-14.28571%) RAM Accesses: 45 (+4.651163%) Estimated Cycles: 55370 (+0.164619%)
bench_play_game_100 Instructions: 313 (+6.101695%) L1 Accesses: 416 (+6.940874%) L2 Accesses: 2 (No change) RAM Accesses: 13 (No change) Estimated Cycles: 881 (+3.161593%)
bench_play_game_1_000_000 Instructions: 409 (+4.603581%) L1 Accesses: 512 (+5.567010%) L2 Accesses: 2 (No change) RAM Accesses: 13 (No change) Estimated Cycles: 977 (+2.842105%) Finished bench [optimized] target(s) in 0.07s Running unittests src/lib.rs (target/release/deps/game-13f4bad779fbfde4)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Bencher New Report:...View results:- bench_play_game (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=077449e5-5b45-4c00-bdfb-3a277413180d&start_time=1697224006000&end_time=1699816009000&upper_boundary=true- bench_play_game_100 (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=96508869-4fa2-44ac-8e60-b635b83a17b7&start_time=1697224006000&end_time=1699816009000&upper_boundary=true- bench_play_game_1_000_000 (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=ff014217-4570-42ea-8813-6ed0284500a4&start_time=1697224006000&end_time=1699816009000&upper_boundary=trueこの素敵なウサギが私にくれた便利なタイムマシンを使って、私たちがずっとBencherを使っていたらどうなっていたかを時間を遡って再現しました。 最初にバギーなFizzBuzzFibonacciの実装をプッシュしたところを見ることができます。 私のプルリクエストに対するコメントとしてCIで直ちに失敗が出ました。 その同じ日に、私はその無駄な、余分なループを取り除くことでパフォーマンスのバグを修正しました。 火事はありません。ただ幸せなユーザーたちだけです。
Bencher: 連続ベンチマーキング
Bencherは、連続ベンチマーキングツールのスイートです。 パフォーマンスの後退があなたのユーザーに影響を与えたことはありますか? Bencherなら、それが起こるのを防げた可能性があります。 Bencherは、パフォーマンスの低下を_productionに到達する_前に検出し、防止することを可能にします。
- 実行: お気に入りのベンチマーキングツールを使用してベンチマークをローカルまたはCIで実行します。
bencherCLIは単にあなたの既存のベンチマークハーネスをラップし、その結果を保存します。 - 追跡: ベンチマークの結果を時間と共に追跡します。ソースブランチ、テストベッド、測定基準に基づいてBencherのWebコンソールを使用して結果を監視、クエリ、グラフ化します。
- キャッチ: CIでパフォーマンスの後退をキャッチします。Bencherは最先端のカスタマイズ可能な分析を使用して、パフォーマンスの後退がProductionに到達する前にそれを検出します。
機能の後退を防ぐためにユニットテストがCIで実行されるのと同じ理由で、Bencherを使用してCIでベンチマークを実行してパフォーマンスの後退を防ぐべきです。パフォーマンスのバグはバグです!
CIでパフォーマンスの回帰を捉えるのを開始してください - Bencher Cloudを無料で試す。