これは何?
様々な言語でMap, Filter, Reduceを実現してみた(1)の記事を見てRust
とRuby
でやってみたというだけの記事です。
環境
- MacBook Pro (2018)
- macOS Mojave 10.14.4
- rustc 1.34.1
- ruby 2.6.2p47
コード
※制限事項「生成したデータはイテレータオブジェクトではなく、実体データの集合になるようにした」に則ってコードを修正済み
Rust
fn main() {
let a = (0..10_000_000).collect::<Vec<i64>>();
let res = a
.iter()
.map(|a| a * 2)
.filter(|&a| a % 3 == 0)
.fold(0, |acc, x| acc + x);
println!("{}", res);
}
Ruby
class Integer
def twice
self * 2
end
def div3?
self % 3 == 0
end
end
a = (0..10_000_000 - 1).to_a
puts a.map(&:twice).select(&:div3?).sum
計測
mfr.rs
およびmfr.rb
というファイル名にしました。
Rustの場合
$ cargo b --bin mfr --release
$ time ./target/release/mfr
33333336666666
real 0m0.016s
user 0m0.012s
sys 0m0.003s
環境差異があるとはいえ、元記事のC++
のケースより速いのはなぜでしょう…?
(追記)ルール違反してた。
- 生成したデータはイテレータオブジェクトではなく、実体データの集合になるようにした
というわけで、Rangeオブジェクトのままmap
/filter
/fold
するのではなく、一旦Vec<i64>
にcollect
してから実行するように修正。Rubyも同様。
$ time for i in `seq 1 1000`; do ./target/release/mfr ; done
(中略)
33333336666666
real 1m4.037s
user 0m29.641s
sys 0m31.654s
1回あたりの処理にかかる時間はおよそ12msec。標準出力に吐く処理で3msecぐらい(文字列を出力するプログラムで測った)なので、コンパイル時に計算しているわけではなさそうです。
だいたい1処理あたり64msですね。それでも微妙に元記事のC++
のケースより少し速い。
Rubyの場合
上記環境での1回の実行で1.9秒ぐらいかかった。
$ time ruby mfr.rb
33333336666666
real 0m1.946s
user 0m1.777s
sys 0m0.129s