Rustで乱数を生成するときに rand
クレイトを使用しますが、無意識に rand::thread_rng()
を使ってしまったりします。
暗号の強度より処理速度を優先したい場合、他の乱数生成アルゴリズムを使うと良い場面があります。
そこでrand
クレイトで使えるアルゴリズムの処理速度を比較してみました。
前提条件
$ system_profiler -detailLevel mini | grep "Hardware Overview" -n12 | tail -n15
123-Hardware:
124-
125: Hardware Overview:
126-
127- Model Name: MacBook Pro
128- Model Identifier: MacBookPro12,1
129- Processor Name: Intel Core i5
130- Processor Speed: 2.7 GHz
131- Number of Processors: 1
132- Total Number of Cores: 2
133- L2 Cache (per Core): 256 KB
134- L3 Cache: 3 MB
135- Memory: 16 GB
136- Boot ROM Version: MBP121.0167.B33
137- SMC Version (system): 2.28f7
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.12.6
BuildVersion: 16G29
$ rustc --version
rustc 1.22.0-nightly (01c65cb15 2017-09-20)
ベンチマーク結果
結論から言うと、処理速度を優先したい場合はrand::XorShiftRng
を使うのがよいようです。
test bench_rng_chacha ... bench: 31 ns/iter (+/- 2)
test bench_rng_isaac ... bench: 16 ns/iter (+/- 3)
test bench_rng_isaac64 ... bench: 7 ns/iter (+/- 0)
test bench_rng_osrng ... bench: 1,119 ns/iter (+/- 110)
test bench_rng_random ... bench: 55 ns/iter (+/- 5)
test bench_rng_std ... bench: 7 ns/iter (+/- 0)
test bench_rng_thread_rng ... bench: 48 ns/iter (+/- 3)
test bench_rng_xorshift ... bench: 5 ns/iter (+/- 1)
ベンチマークのソースコード
#![feature(test)]
extern crate test;
extern crate rand;
use test::Bencher;
use rand::{random, thread_rng, Rng, XorShiftRng, ChaChaRng, IsaacRng, Isaac64Rng, OsRng, StdRng};
#[bench]
fn bench_rng_random(b: &mut Bencher) {
b.iter(|| {
random::<f64>()
});
}
#[bench]
fn bench_rng_thread_rng(b: &mut Bencher) {
let mut rng = thread_rng();
b.iter(|| {
rng.gen_range(0., 1.)
});
}
#[bench]
fn bench_rng_xorshift(b: &mut Bencher) {
let mut rng = XorShiftRng::new_unseeded();
b.iter(|| {
rng.gen_range(0., 1.)
});
}
#[bench]
fn bench_rng_chacha(b: &mut Bencher) {
let mut rng = ChaChaRng::new_unseeded();
b.iter(|| {
rng.gen_range(0., 1.)
});
}
#[bench]
fn bench_rng_isaac(b: &mut Bencher) {
let mut rng = IsaacRng::new_unseeded();
b.iter(|| {
rng.gen_range(0., 1.)
});
}
#[bench]
fn bench_rng_isaac64(b: &mut Bencher) {
let mut rng = Isaac64Rng::new_unseeded();
b.iter(|| {
rng.gen_range(0., 1.)
});
}
#[bench]
fn bench_rng_osrng(b: &mut Bencher) {
let mut rng = OsRng::new().expect("OsRng::new() error.");
b.iter(|| {
rng.gen_range(0., 1.)
});
}
#[bench]
fn bench_rng_std(b: &mut Bencher) {
let mut rng = StdRng::new().expect("StdRng::new() error.");
b.iter(|| {
rng.gen_range(0., 1.)
});
}