TL;DR
本稿で述べるような処理においてはループの中でif
を使用した方が速いという話.
本題
filter()
を用いた以下のコードは1以上100未満の偶数を出力する.
for i in (1..100).filter(|&x| x % 2 == 0) {
println!("{}", i);
}
本稿では0 ... 10000
という等差数列vec
の要素のうち,偶数である要素を足し合わせる処理したい.
const MAX: u32 = 10000;
fn main() {
let vec: Vec<u32> = (0..MAX).collect();
}
そこで,filter()
を用いた/用いない場合についてその速度を比較する.
filter()
を使わずにif
を使用した場合
let mut result = 0;
for i in &vec {
if i % 2 == 0{
result += i;
}
}
filter()
を用いる
イテレータとしてループで使用した場合
let mut result = 0;
for i in vec.iter().filter(|&x| x % 2 == 0) {
result += i;
}
ループ前に使用した場合
let mut result = 0;
let vec: Vec<u32> = vec.iter().cloned().filter(|&x| x % 2 == 0).collect();
for i in &vec {
result += i;
}
数値解析
10000回繰り返すのにかかる時間を計測した.
ソースコード
use std::time::{Instant};
const TRIAL: u32 = 10000;
const MAX: u32 = 10000;
fn main() {
let vec: Vec<u32> = (0..MAX).collect();
// (a)
let mut result = 0;
let start = Instant::now();
for _ in 0..TRIAL{
for i in &vec {
if i % 2 == 0{
result += i;
}
}
}
println!("\n(a) Result: {}, elapsed:{:?}", result, start.elapsed());
// (b)
result = 0;
let start = Instant::now();
for _ in 0..TRIAL{
for i in vec.iter().filter(|&x| x % 2 == 0) {
result += i;
}
}
println!("\n(b) Result: {}, elapsed:{:?}", result, start.elapsed());
// (c)
result = 0;
let start = Instant::now();
for _ in 0..TRIAL{
let vec: Vec<u32> = vec.iter().cloned().filter(|&x| x % 2 == 0).collect();
for i in &vec {
result += i;
}
}
println!("\n(c) Result: {}, elapsed:{:?}", result, start.elapsed());
}
結果
$ cargo run --release
Compiling filter_if v0.1.0 (/Users/scstechr/Works/projects/filter_if)
Finished release [optimized] target(s) in 1.88s
Running `target/release/filter_if`
(a) Result: 841896832, elapsed:124.80438ms
(b) Result: 841896832, elapsed:130.393264ms
(c) Result: 841896832, elapsed:157.500689ms
結論
今回の場合においてはループの中でif
分岐した方が速い.
(というよりfilter()
は遅い?)
todo
cargo
にはベンチマーク機能があるらしいので,そちらを記事に追加して更新する.