質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

1856閲覧

benchテストでの変数の寿命+ArrayをStackとHeapに置いた時の差

namuyan

総合スコア76

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2019/02/26 01:36

Rustのチュートリアルが完了してからツールを自作できるまで慣れました。
Pyo3の関係でcargo 1.34.0-nightlyを使用しています。

今はNightlyに付属しているベンチ機能を用いてオブジェクトのコスト把握を行っています。
以下のようなコードを用いてスタック領域のArrayとヒープ領域のArrayの速度差を調べようとしました。
しかしコンパイルには失敗します。

rust

1#![feature(test)] 2extern crate test; 3 4#[cfg(test)] 5mod tests { 6 use super::test::Bencher; 7 8 #[bench] 9 fn box_ref(b: &mut Bencher){ 10 let mut data = Box::new([0u8;32768*16]); 11 b.iter( move || {data.iter_mut().map(|x| *x += 1)}); 12 } 13 14 #[bench] 15 fn array_ref(b: &mut Bencher){ 16 let mut data = [0u8;32768*16]; 17 b.iter(move || data.iter_mut().map(|x| *x += 1)); 18 } 19}

そこで以下の様に変更し計測を行いました。
結果として、Boxが33,809 ns/iter (+/- 10,772)、Arrayが11,342 ns/iter (+/- 509)です。奇妙なのは data.iter_mut...の行の数に依らず計測結果が変わりませんでした。アセンブラを確認しましたが最適化により削除されたのかよくわかりません。

rust

1#[cfg(test)] 2mod tests { 3 use super::test::Bencher; 4 5 #[bench] 6 fn box_ref(b: &mut Bencher){ 7 fn work() -> Box<[u8;32768*16]> { 8 let mut data = Box::new([0u8;32768*16]); 9 data.iter_mut().map(|x| *x += 1); 10 data 11 } 12 b.iter(|| work()); 13 } 14 15 #[bench] 16 fn array_ref(b: &mut Bencher){ 17 fn work() -> [u8;32768*16] { 18 let mut data = [0u8;32768*16]; 19 data.iter_mut().map(|x| *x += 1); 20 data 21 } 22 b.iter(|| work()); 23 } 24}

error

1error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements 2 --> src\lib.rs:46:30 3 | 446 | b.iter(move || {data.iter_mut().map(|x| *x += 1)}); 5 | ^^^^^^^^ 6 | 7note: first, the lifetime cannot outlive the lifetime as defined on the body at 46:16...

iterの中にiterを含む事で予測できなくなるのか、原因がよくわからず困っています。
もしこの問題がわかる方がおられましたら、または二つの速度差についてわかる方がおられましたら、
回答の方を宜しくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

奇妙なのは data.iter_mut...の行の数に依らず計測結果が変わりませんでした。

Rustに慣れてきても間違えることがあるのですが、イテレータのmapメソッドは、forループと違いその場で処理は行ってくれません。実際には新しいイテレータを返します。イテレータを作るだけなら一瞬で終わるので、計測結果に変化が現れなかったわけです。

処理を実行させるにはmapが返したイテレータに対して、collect()sum()などのイテレータを消費するメソッドを適用する必要があります。また、mapの代わりにfor_eachを使うこともできます。

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements

iterの中にiterを含む事で予測できなくなるのか、原因がよくわからず困っています。

mapが返したイテレータをクロージャの戻り値として返そうとしているためエラーになっています。iter_mut()dataの可変の参照をとります(selfの参照を自動的にとるのでautorefと呼んでます。型強制type coercionという振る舞いの一種です)。しかし、その参照をクロージャの外に渡そうとしているためにライフタイムの要件が競合しているのです(due to conflicting requirements)。

以下のようにmapfor_eachに変えるとうまくいきます。

rust

1#![feature(test)] 2extern crate test; 3 4#[cfg(test)] 5mod tests { 6 use super::test::Bencher; 7 8 #[bench] 9 fn box_ref(b: &mut Bencher) { 10 let mut data = Box::new([0u8;32768*16]); 11 b.iter(move || { 12 data.iter_mut().for_each(|x| *x += 1); 13 // data.iter_mut().for_each(|x| *x += 1); 14 }); 15 } 16 17 #[bench] 18 fn array_ref(b: &mut Bencher){ 19 let mut data = [0u8;32768*16]; 20 b.iter(move || { 21 data.iter_mut().for_each(|x| *x += 1); 22 // data.iter_mut().for_each(|x| *x += 1); 23 }); 24 } 25}

実行結果

console

1$ cargo bench 2 Compiling hello-bench v0.1.0 (... /hello-bench) 3 Finished release [optimized] target(s) in 0.75s 4 Running target/release/deps/hello_bench-27b84eca8ab51126 5 6running 2 tests 7test tests::array_ref ... bench: 22,288 ns/iter (+/- 5,191) 8test tests::box_ref ... bench: 23,993 ns/iter (+/- 6,710) 9 10test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured; 0 filtered out 11 12# コードのコメントを外してfor_eachを2回ずつ実行 13$ cargo bench 14 Compiling hello-bench v0.1.0 (... /hello-bench) 15 Finished release [optimized] target(s) in 0.68s 16 Running target/release/deps/hello_bench-27b84eca8ab51126 17 18running 2 tests 19test tests::array_ref ... bench: 45,611 ns/iter (+/- 10,786) 20test tests::box_ref ... bench: 47,061 ns/iter (+/- 7,599) 21 22test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured; 0 filtered out

なお、実際のアプリケーションでは、その前後の処理もあわせて最適化がかかりますので、マイクロベンチマークで測った単体の機能の性能は、ほとんど参考になりません。また、Bencher自体もLLVMの過度な最適化を抑止するような技法を使っているはずですので、その結果をどこまで信用していいのかは私もよくわかりません。

基本的にはプロファイリングなどの手法を使って、アプリケーションのレベルで性能測定することをおすすめします。

たとえば、以下の記事では、macOSのdtraceで収集した情報をflame graphにする方法を紹介しています。

Linuxだとdtraceの代わりにperfが使えたと思います。(私はメインのマシンがFreeBSDとmacOSなのでdtraceしか使っておらず、perfのことはよくわかりません。またWindowsでどうするかもわかりません)


追記

書き忘れましたが、Box::new([0u8;32768*16])のように固定長(サイズが固定)の配列をヒープに置くようなことは普通はしません。スタックに置くのと違って、ヒープに置くときは実行時にサイズを決められますので、このような用途には可変長のベクタ(Vec<T>)を使います。

Box::new([0u8;32768*16])とすると、まずスタック上の配列が初期化され、それがBox::newによってヒープにコピーされますので非効率な処理になります(上のベンチマークではその部分にかかる時間は測定の範囲外になっています) Vec<T>なら最初からヒープ領域が初期化されます。

あとVec<T>pushpopはいらないけれどヒープに置きたいというときは、boxed slice(Box<[T]>)というものもあります。これはVec<T>から変換して作ります。

rust

1 #[bench] 2 // Vec<T> 3 fn vec(b: &mut Bencher) { 4 let mut data = vec![0u8;32768*16]; 5 b.iter(move || { 6 data.iter_mut().for_each(|x| *x += 1); 7 // data.iter_mut().for_each(|x| *x += 1); 8 }); 9 } 10 11 #[bench] 12 // Box<[T]> 13 fn boxed_slice(b: &mut Bencher) { 14 let mut data = vec![0u8;32768*16].into_boxed_slice(); // Box<[u8]>型 15 b.iter(move || { 16 data.iter_mut().for_each(|x| *x += 1); 17 // data.iter_mut().for_each(|x| *x += 1); 18 }); 19 }

投稿2019/02/26 03:45

編集2019/02/26 04:12
tatsuya6502

総合スコア2035

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namuyan

2019/02/27 11:51

解答して頂き有難うございます。実際のコードではスタック領域を使い切ってしまう為、 Nightlyの機能なので好まれませんが`feature(box_syntax)`を使用しています。この場合はコピーを介さず作られるという事でしょうか?
tatsuya6502

2019/02/27 12:08

あー、このBox::new()の振る舞いって以前からそうだった気がするので、そういう仕様だと思っていたのですが、こちらによるとバグらしいです。box syntaxを使ったときは意図した動作になって、コピーを介さず作られるそうです。https://github.com/rust-lang/rust/issues/58570
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問