Rustでプログラムをベンチマークするにはどうすればよいですか?たとえば、プログラムの実行時間を秒単位で取得するにはどうすればよいですか?
2年後(このページを偶然見つけたRustプログラマを支援するため)、テストスイートの一部としてRustコードをベンチマークするツールが存在することに注意する価値があります。
(以下のガイドリンクから)#[bench]
属性を使用すると、標準のRustツールを使用してコードのメソッドをベンチマークできます。
extern crate test;
use test::Bencher;
#[bench]
fn bench_xor_1000_ints(b: &mut Bencher) {
b.iter(|| {
// use `test::black_box` to prevent compiler optimizations from disregarding
// unused values
test::black_box(range(0u, 1000).fold(0, |old, new| old ^ new));
});
}
コマンドcargo bench
の場合、次のように出力されます。
running 1 test
test bench_xor_1000_ints ... bench: 375 ns/iter (+/- 148)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
リンク:
単にコードの時間を計りたい場合は、 time
crateを使用できます。 時間は非推奨 、しかし。フォローアップクレートは chrono
です。
追加 time = "*"
あなたのCargo.toml
。
追加
extern crate time;
use time::PreciseTime;
メイン関数の前に
let start = PreciseTime::now();
// whatever you want to do
let end = PreciseTime::now();
println!("{} seconds for whatever you did.", start.to(end));
[package]
name = "hello_world" # the name of the package
version = "0.0.1" # the current version, obeying semver
authors = [ "[email protected]" ]
[[bin]]
name = "Rust"
path = "Rust.rs"
[dependencies]
Rand = "*" # Or a specific version
time = "*"
extern crate Rand;
extern crate time;
use Rand::Rng;
use time::PreciseTime;
fn main() {
// Creates an array of 10000000 random integers in the range 0 - 1000000000
//let mut array: [i32; 10000000] = [0; 10000000];
let n = 10000000;
let mut array = Vec::new();
// Fill the array
let mut rng = Rand::thread_rng();
for _ in 0..n {
//array[i] = rng.gen::<i32>();
array.Push(rng.gen::<i32>());
}
// Sort
let start = PreciseTime::now();
array.sort();
let end = PreciseTime::now();
println!("{} seconds for sorting {} integers.", start.to(end), n);
}
タイミングテストには、 std::time::Instant
fn my_function() {
use std::time::Instant;
let now = Instant::now();
{
my_function_to_measure();
}
let elapsed = now.elapsed();
let sec = (elapsed.as_secs() as f64) + (elapsed.subsec_nanos() as f64 / 1000_000_000.0);
println!("Seconds: {}", sec);
}
fn main() {
my_function();
}
もちろん、これを頻繁に行う場合は、変換を一般化するか、このためのユーティリティを提供するクレートを使用するか、またはInstant
とDuration
を独自の関数でラップして、より冗長な方法。
実装言語に関係なく、プログラムの実行時間を調べる簡単な方法は、time prog
コマンドラインで。例えば:
~$ time sleep 4
real 0m4.002s
user 0m0.000s
sys 0m0.000s
最も興味深い測定値は通常user
です。これは、システムで何が起こっているかに関係なく、プログラムによって行われた実際の作業量を測定します(sleep
はベンチマークするのに退屈なプログラムです)。 real
は経過した実際の時間を測定し、sys
はプログラムに代わってOSが実行した作業量を測定します。
現在、以下のLinux機能のいずれにもインターフェースはありません。
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)
getrusage
times
(マンページ:man 2 times
)LinuxでRustプログラムのCPU時間とホットスポットを測定する方法は次のとおりです。
/usr/bin/time program
perf stat program
perf record --freq 100000 program; perf report
valgrind --tool=callgrind program; kcachegrind callgrind.out.*
perf report
およびvalgrind
の出力は、プログラム内のデバッグ情報の可用性に依存します。動作しない場合があります。
このために小さな箱を作成しました( measure_time )。これは、スコープの終了までの時間を記録または出力します。
#[macro_use]
extern crate measure_time;
fn main() {
print_time!("measure function");
do_stuff();
}
いくつかの方法がありますRustプログラムをベンチマークします。ほとんどの実際のベンチマークについては、適切なベンチマークフレームワークを使用する必要があります。 (統計分析を含む)簡単に台無しになることもあります。一番下の「なぜベンチマークを書くのが難しいのか」セクションもお読みください!
Instant
およびDuration
コードの実行時間をすばやく確認するには、 std::time
の型を使用できます。モジュールはかなり最小限ですが、単純な時間測定には適しています。 Instant
の代わりに SystemTime
を使用する必要があります。前者は単調に増加するクロックであり、後者はそうではないためです。例( Playground ):
use std::time::Instant;
let before = Instant::now();
workload();
println!("Elapsed time: {:.2?}", before.elapsed());
残念ながら、stdのInstant
の精度はドキュメントでは指定されていませんが、すべての主要なオペレーティングシステムでは、プラットフォームが提供できる最高の精度を使用します(これは通常約20ns前後です)。
std::time
で十分な機能が提供されない場合は、 chrono
をご覧ください。ただし、期間を測定するために、その外部クレートが必要になることはほとんどありません。
フレームワークを使用すると、特定の間違いを防ぐことができるため、多くの場合、良いアイデアです。
Rustには便利な組み込みのベンチマーク機能がありますが、残念ながら2019-07の時点ではまだ不安定です。 #[bench]
属性を関数に追加し、1つの&mut test::Bencher
引数を受け入れるようにする必要があります。
#![feature(test)]
extern crate test;
use test::Bencher;
#[bench]
fn bench_workload(b: &mut Bencher) {
b.iter(|| workload());
}
cargo bench
を実行すると印刷されます:
running 1 test
test bench_workload ... bench: 78,534 ns/iter (+/- 3,606)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out
クレート criterion
は安定版で実行されるフレームワークですが、組み込みソリューションよりも少し複雑です。より高度な統計分析を行い、より豊富なAPIを提供し、より多くの情報を生成し、プロットを自動的に生成することさえできます。
Criterionの使用方法の詳細については、 「クイックスタート」セクション を参照してください。
ベンチマークを作成する場合、多くの落とし穴があります。 1つの間違いは、ベンチマーク結果を無意味なものにするだけです。一般的な間違いのリストは次のとおりです。
rustc -O3
またはcargo build --release
。 cargo bench
を使用してベンチマークを実行すると、Cargoは最適化を自動的に有効にします。多くの場合、最適化されたコードと最適化されていないコードのパフォーマンスに大きな違いがあるため、この手順は重要です。Rustコード。ベンチマークが完全に削除されていないことを確認してください:ベンチマークは本質的に非常に人工的なものです。通常、期間のみを測定するため、ワークロードの結果は検査されません。ただし、これは、副作用がないため(時間の経過は別として)、優れたオプティマイザーがベンチマーク全体を削除できることを意味します。したがって、オプティマイザをだますためには、何らかの方法で結果値を使用して、ワークロードを削除できないようにする必要があります。簡単な方法は、結果を印刷することです。より良い解決策は black_box
のようなものです。この関数は基本的にLLVMから値を隠します。LLVMは値で何が起こるかを知ることができないからです。何も起こりませんが、LLVMは知りません。そこが肝心だ。
優れたベンチマークフレームワークは、いくつかの状況でブロックボックスを使用します。たとえば、iter
メソッド(組み込みとCriterion Bencher
の両方)に指定されたクロージャは、値を返すことができます。その値は自動的にblack_box
に渡されます。
black_box
に渡します。