web-dev-qa-db-ja.com

Rustのベンチマークプログラム

Rustでプログラムをベンチマークするにはどうすればよいですか?たとえば、プログラムの実行時間を秒単位で取得するにはどうすればよいですか?

46
php--

この回答は時代遅れです!最新の情報については、以下の回答をご覧ください。


time crate を使用して、プログラム内の個々のコンポーネントのタイミングを試すことができます。

11
Kevin Cantu

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

リンク:

83
Michael Tang

単にコードの時間を計りたい場合は、 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));

完全な例

Cargo.toml

[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 = "*"

Rust.rs

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);
}
41
Martin Thoma

タイミングテストには、 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();
}

もちろん、これを頻繁に行う場合は、変換を一般化するか、このためのユーティリティを提供するクレートを使用するか、またはInstantDurationを独自の関数でラップして、より冗長な方法。

17
ideasman42

実装言語に関係なく、プログラムの実行時間を調べる簡単な方法は、time progコマンドラインで。例えば:

~$ time sleep 4

real    0m4.002s
user    0m0.000s
sys     0m0.000s

最も興味深い測定値は通常userです。これは、システムで何が起こっているかに関係なく、プログラムによって行われた実際の作業量を測定します(sleepはベンチマークするのに退屈なプログラムです)。 realは経過した実際の時間を測定し、sysはプログラムに代わってOSが実行した作業量を測定します。

8
Paul Stansifer

現在、以下の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の出力は、プログラム内のデバッグ情報の可用性に依存します。動作しない場合があります。

5
user811773

このために小さな箱を作成しました( measure_time )。これは、スコープの終了までの時間を記録または出力します。

#[macro_use]
extern crate measure_time;
fn main() {
    print_time!("measure function");
    do_stuff();
}
2
Pascalius

いくつかの方法があります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の組み込みベンチマークフレームワーク(夜間のみ)

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 --releasecargo benchを使用してベンチマークを実行すると、Cargoは最適化を自動的に有効にします。多くの場合、最適化されたコードと最適化されていないコードのパフォーマンスに大きな違いがあるため、この手順は重要です。Rustコード。
  • ワークロードを繰り返します:ワークロードを一度実行するだけでほとんど常に役に立たなくなります。タイミングに影響を与える可能性のあるものは多数あります。システム全体の負荷、オペレーティングシステムの処理、CPUスロットル、ファイルシステムキャッシュなどです。したがって、できるだけ頻繁にワークロードを繰り返してください。たとえば、Criterionはすべてのベンチマークを少なくとも5秒間実行します(ワークロードが数ナノ秒しかかかっていない場合でも)。その後、すべての測定時間を分析できます。平均と標準偏差が標準ツールです。
  • ベンチマークが完全に削除されていないことを確認してください:ベンチマークは本質的に非常に人工的なものです。通常、期間のみを測定するため、ワークロードの結果は検査されません。ただし、これは、副作用がないため(時間の経過は別として)、優れたオプティマイザーがベンチマーク全体を削除できることを意味します。したがって、オプティマイザをだますためには、何らかの方法で結果値を使用して、ワークロードを削除できないようにする必要があります。簡単な方法は、結果を印刷することです。より良い解決策は black_box のようなものです。この関数は基本的にLLVMから値を隠します。LLVMは値で何が起こるかを知ることができないからです。何も起こりませんが、LLVMは知りません。そこが肝心だ。

    優れたベンチマークフレームワークは、いくつかの状況でブロックボックスを使用します。たとえば、iterメソッド(組み込みとCriterion Bencherの両方)に指定されたクロージャは、値を返すことができます。その値は自動的にblack_boxに渡されます。

  • 定数値に注意してください:上記のポイントと同様に、ベンチマークで定数値を指定すると、オプティマイザーはその値専用のコードを生成する場合があります。極端な場合には、ワークロード全体が定数に折り畳まれて単一の定数になる可能性があり、これはベンチマークが役に立たないことを意味します。 LLVMが過度に最適化されないように、すべての定数値をblack_boxに渡します。
  • 測定オーバ​​ーヘッドに注意してください:継続時間の測定には時間がかかります。通常は数十ナノ秒ですが、測定時間に影響を与える可能性があります。そのため、数十ナノ秒よりも高速なすべてのワークロードでは、各実行時間を個別に測定しないでください。ワークロードを100回実行し、100回すべての実行にかかった時間を測定できます。それを100で除算すると、1回の平均時間が得られます。上記のベンチマークフレームワークもこのトリックを使用します。 Criterionには、副作用のある(何かを変更するなど)非常に短いワークロードを測定するためのいくつかの方法もあります。
  • 他の多くのこと:残念ながら、ここですべての困難をリストすることはできません。本格的なベンチマークを作成する場合は、オンラインリソースをご覧ください。
0