I am using a SeedableRng
to make reproducible simulations.
Here's my get_rng
function:
use rand::prelude::*;
use rand_xoshiro::rand_core::SeedableRng;
use rand_xoshiro::Xoshiro256StarStar;
pub fn get_rng() -> impl Rng {
// xoshiro; global rng
// this is how the original worked
// and we repeat that here
let rng1 = Xoshiro256StarStar::from_seed([
18, 25, 1, 14, 18, 1, 13, 16, 5, 18, 19, 1, 4, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0,
]);
return rng1;
}
This portion works great.
My simulation function, roughly:
pub fn simulation(&mut self, input: &Vec<SimInput>, rng: &mut dyn RngCore) -> () {
// ...
self.integrate_step(input, &opts, rng);
// ...
}
pub fn integrate_step(
&self,
input: &Vec<SimInput>,
opts: &SimOpts,
rng: &mut dyn RngCore,
) -> f64 {
// ...
}
This will also forward the rng
to some other methods.
The integrate_step
uses StandardNormal and Standard samples:
let n01: f64 = rng.sample(StandardNormal);
let u01: f64 = rng.sample(Standard);
// ... math with n01 and u01
Now the question!
I want to abstract out all of the sample StandardNormal and Standard and the crazy &mut dyn RngCore
out from the simulation code.
I came up with this:
pub struct RngProvider {
rng: Box<dyn RngCore>, // <--- box
}
impl RngProvider {
pub fn new() -> RngProvider {
let b = Box::new(get_rng());
return RngProvider { rng: b };
}
/// gets standard
pub fn u01(&mut self) -> f64 {
return self.rng.sample(Standard);
}
/// gets standard normal
pub fn n01(&mut self) -> f64 {
return self.rng.sample(StandardNormal);
}
}
Is this good?
Then it's used like:
// previously in a bootstrap file ...
let mut rng_provider = RngProvider::new();
pub fn simulation(&mut self, input: &Vec<OuInput>, rng: &mut RngProvider) -> () {
// ...
}
// later in integrate_step...
let n01: f64 = rng.n01();
let u01: f64 = rng.u01();
Performance:
- The
RngCore
version is run time5833 ms
- The
RngProvider
version is run time8300 ms
- Individual compute steps have a ~
200 ms
difference, and that will grow as the input grows.
The nicer abstraction is 30% slower, with the only change being this new struct and the alternative calls. This is run on the same SimInput, same machine, in release mode, repeatedly.
I think it is because of the Box<dyn RngCore>
in the struct. I am not sure what I can do as an alternative.