I need to create a global mutable random number generator. This is for efficiency purposes as thread_rng() is too slow to call, and it's returning the type I want. (I'm aware of the safety implications of having global singletons; I'm using just a single-thread.)
Ideally I want the SmallRng, which is fast, but it doesn't appear to exist in the create (is this perhaps a docs problem -- there's no version so I couldn't check). For now I'll be happy with a XorShiftRng.
Something like:
static mut global_rng : XorShiftRng = ??
I know you can't call functions here so I instead tried wrapping it in an Option and function:
The place in the code that needs the generator is deep within the tree and not logically related to the high-level functions that invoke it. Adding a context, or passing the generator, down through the call-tree would be prohibitive and result in unclean code.
Is the thread-localness the performance problem with rand::thread_rng or the choice of algorithm? If it's the second, the simplest route is to use a thread local of your own rather than a static.
Both in this case. The lookup time for the tread-local is high, as well as the algorithm being slow. I've timed both of them. The lookup is more costly, but the algorithm itself is also higher than desired. In the application I only need rough pseudo-randomness to apply a monte-carlo algorithm. My fallback would thus be a few static mut variables to create a dumb algorithm. I was hoping instead to make use of XorShift, or the SmallRng, instead.
I was also hoping to understand how to create such global mutable values, as they do come up in other contexts as well.
I use random numbers quite a bit in parallel simulations that I'd like to be repeatable (seedable) regardless of threading, so for anyone that does need thread safety and doesn't want to pass around a RNG reference, here's an example of what I do:
#![feature(thread_local)]
extern crate rand;
extern crate xorshift;
use rand::distributions::normal::StandardNormal;
use xorshift::{Rand,Rng,SeedableRng,SplitMix64,Xoroshiro128};
#[thread_local]
static mut RNG:Option<Xoroshiro128> = None;
pub fn seed(x:u64) {
let mut seeding_rng:SplitMix64 = SeedableRng::from_seed(x);
unsafe { RNG = Some(Rand::rand(&mut seeding_rng)); }
}
pub fn rnorm() -> f64 {
unsafe {
let StandardNormal(x) = RNG.as_mut().unwrap().gen::<StandardNormal>();
x
}
}
pub fn rnormv<R:DimName,C:DimName>() -> MatrixMN<f64,R,C>
where DefaultAllocator:Allocator<f64,R,C> {
unsafe { MatrixMN::<f64,R,C>::from_iterator(RNG.as_mut().unwrap().gen_iter().map(|x| { let StandardNormal(y) = x; y })) }
}
I'm limited to fixed versions on the target platform. I'm likely to stop using XorShiftRng as well since it's also slower than desired. I'll probably fall back to a trivial pseudo-random algorithm -- which is all that is needed in this case.