参考
C#のLinqでGroupByを使っているのならPythonの方が2倍速くなる - Qiita
C# の Linq が python の2倍遅い、は嘘 - Qiita
コード
# [macro_use]
extern crate serde_derive;
# [macro_use]
extern crate serde_json;
extern crate csv;
extern crate itertools;
extern crate serde;
use std::fs::File;
//use std::io::prelude::*;
use itertools::Itertools;
use serde::Serializer;
fn main() {
#[derive(Debug, Deserialize)]
struct Record(String, String, f64, f64);
let mut rdr = csv::Reader::from_reader(File::open("test.csv").expect("File open err."));
let multiply_to_int = |x: f64, y: f64| if x > 0.0 {
(x * y + 0.0000001) as i32
} else {
(x * y - 0.0000001) as i32
};
let mut ser = serde_json::Serializer::new(File::create("result.json").expect("File open err."));
ser.collect_seq(rdr.deserialize()
.map(|result| result.expect("Bad CSV"))
.map(|record: Record| (record.0.to_owned(), multiply_to_int(record.2, record.3)))
.into_group_map()
.into_iter()
.map(|(k, v)| json!({"a": k, "z": v.iter().sum::<i32>()})))
.expect("Serialize err.");
}
実行時間
Python (元記事のを使用)
$ python app.py
所要時間:0.4632906913757324[sec]
$ python app.py
所要時間:0.44978904724121094[sec]
$ python app.py
所要時間:0.4565582275390625[sec]
Rust
$ time ./target/release/foo
real 0m0.339s
user 0m0.331s
sys 0m0.007s
$ time ./target/release/foo
real 0m0.340s
user 0m0.330s
sys 0m0.010s
$ time ./target/release/foo
real 0m0.338s
user 0m0.328s
sys 0m0.010s
感想
- 速くなるのは分かっててやったので、速度については、こんなもんか程度の感想。
- イテレータ使い倒したらLinqとそんなに遜色ないコーディング量で書けて、別にそれで遅くなるわけでもないのはRust尊いなーって印象。
- コンパイル時のライフタイム地獄にはあんまり陥らなかった。型が主に
i32
とf64
だったのとString
はto_owned()
で逃げたためと思われる。 - それでもやっぱり、型を合わせるのがしんどかった。
- serde_json、地味に便利。
-
Record
型のString
を&str
にしてコピーを避ける方法、探せばある気がするんだけど、分からなかった。 - itertoolsに
group_by
というそれっぽい関数があるけど、ドキュメント読んだ感じ、SQLのGROUP BY
やlinqのGroupBy
とは挙動違いそう。これ、罠だろ。
ドキュメント中のサンプルコード
// group data into runs of larger than zero or not.
let data = vec![1, 3, -2, -2, 1, 0, 1, 2];
// groups: |---->|------>|--------->|
// Note: The `&` is significant here, `GroupBy` is iterable
// only by reference. You can also call `.into_iter()` explicitly.
for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) {
// Check that the sum of each group is +/- 4.
assert_eq!(4, group.sum::<i32>().abs());
}