Skip to content

Param support. #30

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 12 commits into from
Sep 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ license = "MIT"
default = ["thread_rng"]

# The thread_rng feature requires the rand dependency
thread_rng = ["rand"]
thread_rng = ["rand/getrandom"]

[dependencies]
rand_core = "0.5"
rand = {version = "0.7", optional = true}
rand_core = "0.6"
rand = {version = "0.8", optional = true}

[dev-dependencies]
rand_xorshift = "0.3.0"
rand_core = {version = "0.6", features=["getrandom"]}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ sfmt

Rust implementation of [SIMD-oriented Fast Mersenne Twister (SFMT)] interface using x86-SIMD in `std::arch`.
This is pure rust re-implementation, and tested on Windows/macOS/Linux.
This works with limited parameters (607, 1279, 2281, 4253, 11213, 19937, 44497, 86243, 132049, 216091).

[SIMD-oriented Fast Mersenne Twister (SFMT)]: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/

Limitations
------------

- Supported only on x86 and x86_64 (due to original SFMT)
- Algorithms other than MT19937 are not supported (may be fixed in future release)
- Require rustc >= 1.51

License
--------
Expand Down
2 changes: 2 additions & 0 deletions benches/rand_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ extern crate sfmt;
extern crate test;

use rand::*;
use rand_core::SeedableRng;
use rand_xorshift::*;
use sfmt::SFMT;
use test::Bencher;

Expand Down
215 changes: 152 additions & 63 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//!
//! ```
//! use rand_core::{RngCore, SeedableRng};
//! let mut rng = sfmt::SFMT::seed_from_u64(42);
//! let mut rng = sfmt::SFMT19937::seed_from_u64(42);
//! let r = rng.next_u32();
//! println!("random u32 number = {}", r);
//! ```
Expand All @@ -15,94 +15,183 @@ mod sfmt;
#[cfg(feature = "thread_rng")]
mod thread_rng;

use rand_core::{impls, Error, RngCore, SeedableRng};

use self::packed::*;
#[cfg(feature = "thread_rng")]
pub use self::thread_rng::{thread_rng, ThreadRng};

/// State of SFMT
///
/// This struct implements random number generation through `rand::Rng`.
#[derive(Clone)]
pub struct SFMT {
/// the 128-bit internal state array
state: [i32x4; sfmt::SFMT_N],
/// index counter to the 32-bit internal state array
idx: usize,
}
/// Fall back to [`SFMT19937`], not be a breaking change.
pub type SFMT = SFMT19937;
/// SFMT with a state length 607
pub type SFMT607 = paramed::SFMT<607, { 607 / 128 + 1 }>;
/// SFMT with a state length 1279
pub type SFMT1279 = paramed::SFMT<1279, { 1279 / 128 + 1 }>;
/// SFMT with a state length 2281
pub type SFMT2281 = paramed::SFMT<2281, { 2281 / 128 + 1 }>;
/// SFMT with a state length 4253
pub type SFMT4253 = paramed::SFMT<4253, { 4253 / 128 + 1 }>;
/// SFMT with a state length 11213
pub type SFMT11213 = paramed::SFMT<11213, { 11213 / 128 + 1 }>;
/// SFMT with a state length 19937
pub type SFMT19937 = paramed::SFMT<19937, { 19937 / 128 + 1 }>;
/// SFMT with a state length 44497
pub type SFMT44497 = paramed::SFMT<44497, { 44497 / 128 + 1 }>;
/// SFMT with a state length 86243.
pub type SFMT86243 = paramed::SFMT<86243, { 86243 / 128 + 1 }>;
/// SFMT with a state length 132049.
pub type SFMT132049 = paramed::SFMT<132049, { 132049 / 128 + 1 }>;
/// SFMT with a state length 216091.
pub type SFMT216091 = paramed::SFMT<216091, { 216091 / 128 + 1 }>;

impl SFMT {
fn pop32(&mut self) -> u32 {
let val = extract(self.state[self.idx / 4], self.idx % 4);
self.idx += 1;
val
}
/// Internal implemention of SFMT with `MEXP` parameter.
pub mod paramed {
use crate::{
packed::*,
sfmt::{SfmtParams, SFMTMEXP},
};
use rand_core::{impls, Error, RngCore, SeedableRng};

fn pop64(&mut self) -> u64 {
let p = self.state.as_ptr() as *const u32;
let val = unsafe {
let p = p.offset(self.idx as isize);
*(p as *const u64) // reinterpret cast [u32; 2] -> u64
};
self.idx += 2;
val
/// State of SFMT
///
/// This struct implements random number generation through `rand::Rng`.
/// The MEXP is a parameter that defines a length of state.
/// MEXP is limted to be a known value, and it is checked at compile time.
/// MEXP can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`.
/// Since there is a limitation to const generics, we also need the `MEXP_N = {MEXP / 128 + 1}`
/// ```
/// # use rand_core::SeedableRng;
/// let s = sfmt::SFMT19937::seed_from_u64(23);
/// ```
#[derive(Clone)]
pub struct SFMT<const MEXP: usize, const MEXP_N: usize> {
/// the 128-bit internal state array
pub(crate) state: [i32x4; MEXP_N],
/// index counter to the 32-bit internal state array
pub(crate) idx: usize,
}

fn gen_all(&mut self) {
sfmt::sfmt_gen_rand_all(self);
self.idx = 0;
}
}
impl<const MEXP: usize, const MEXP_N: usize> SFMT<MEXP, MEXP_N>
where
SFMTMEXP<MEXP, MEXP_N>: SfmtParams<MEXP, MEXP_N>,
{
fn pop32(&mut self) -> u32 {
let val = extract(self.state[self.idx / 4], self.idx % 4);
self.idx += 1;
val
}

impl SeedableRng for SFMT {
type Seed = [u8; 4];
fn pop64(&mut self) -> u64 {
let p = self.state.as_ptr() as *const u32;
let val = unsafe {
let p = p.offset(self.idx as isize);
*(p as *const u64) // reinterpret cast [u32; 2] -> u64
};
self.idx += 2;
val
}

fn from_seed(seed: [u8; 4]) -> Self {
let mut sfmt = SFMT {
state: [zero(); sfmt::SFMT_N],
idx: 0,
};
let seed = unsafe { *(seed.as_ptr() as *const u32) };
sfmt::sfmt_init_gen_rand(&mut sfmt, seed);
sfmt
fn gen_all(&mut self) {
SFMTMEXP::<MEXP, MEXP_N>::sfmt_gen_rand_all(self);
self.idx = 0;
}
}
}

impl RngCore for SFMT {
fn next_u32(&mut self) -> u32 {
if self.idx >= sfmt::SFMT_N32 {
self.gen_all();
impl<const MEXP: usize, const MEXP_N: usize> SeedableRng for SFMT<MEXP, MEXP_N>
where
SFMTMEXP<MEXP, MEXP_N>: SfmtParams<MEXP, MEXP_N>,
{
type Seed = [u8; 4];

fn from_seed(seed: [u8; 4]) -> Self {
let mut sfmt = Self {
state: [zero(); MEXP_N],
idx: 0,
};
let seed = unsafe { *(seed.as_ptr() as *const u32) };
SFMTMEXP::<MEXP, MEXP_N>::sfmt_init_gen_rand(&mut sfmt, seed);
sfmt
}
self.pop32()
}

fn next_u64(&mut self) -> u64 {
if self.idx >= sfmt::SFMT_N32 - 1 {
// drop last u32 if idx == N32-1
self.gen_all();
impl<const MEXP: usize, const MEXP_N: usize> RngCore for SFMT<MEXP, MEXP_N>
where
SFMTMEXP<MEXP, MEXP_N>: SfmtParams<MEXP, MEXP_N>,
{
fn next_u32(&mut self) -> u32 {
if self.idx >= SFMTMEXP::<MEXP, MEXP_N>::SFMT_N32 {
self.gen_all();
}
self.pop32()
}
self.pop64()
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
impls::fill_bytes_via_next(self, dest)
}
fn next_u64(&mut self) -> u64 {
if self.idx >= SFMTMEXP::<MEXP, MEXP_N>::SFMT_N32 - 1 {
// drop last u32 if idx == N32-1
self.gen_all();
}
self.pop64()
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
fn fill_bytes(&mut self, dest: &mut [u8]) {
impls::fill_bytes_via_next(self, dest)
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use rand_core::{RngCore, SeedableRng};

#[test]
fn random() {
fn random_607() {
let mut rng = SFMT607::seed_from_u64(0);
for _ in 0..607 * 20 {
// Generate many random numbers to test the overwrap
let r = rng.next_u64();
if r % 2 == 0 {
let _r = rng.next_u32();
} // shift SFMT.idx randomly
}
}
#[test]
fn random_19937() {
let mut rng = SFMT::seed_from_u64(0);
for _ in 0..sfmt::SFMT_N * 20 {
for _ in 0..19937 * 20 {
// Generate many random numbers to test the overwrap
let r = rng.next_u64();
if r % 2 == 0 {
let _r = rng.next_u32();
} // shift SFMT.idx randomly
}
}
#[test]
fn random_44497() {
let mut rng = SFMT44497::seed_from_u64(0);
for _ in 0..44497 * 20 {
// Generate many random numbers to test the overwrap
let r = rng.next_u64();
if r % 2 == 0 {
let _r = rng.next_u32();
} // shift SFMT.idx randomly
}
}
#[test]
fn random_86243() {
let mut rng = SFMT86243::seed_from_u64(0);
for _ in 0..86243 * 20 {
// Generate many random numbers to test the overwrap
let r = rng.next_u64();
if r % 2 == 0 {
let _r = rng.next_u32();
} // shift SFMT.idx randomly
}
}
#[test]
fn random_216091() {
let mut rng = SFMT216091::seed_from_u64(0);
for _ in 0..216091 * 20 {
// Generate many random numbers to test the overwrap
let r = rng.next_u64();
if r % 2 == 0 {
Expand Down
8 changes: 5 additions & 3 deletions src/packed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub(crate) fn extract(vals: i32x4, imm: usize) -> u32 {
1 => _mm_extract_epi32(vals, 1) as u32,
2 => _mm_extract_epi32(vals, 2) as u32,
3 => _mm_extract_epi32(vals, 3) as u32,
_ => unreachable!(),
_ => core::hint::unreachable_unchecked(),
}
}
}
Expand All @@ -35,8 +35,10 @@ pub(crate) fn insert(vals: &mut i32x4, val: i32, imm: usize) {
1 => _mm_insert_epi32(*vals, val, 1),
2 => _mm_insert_epi32(*vals, val, 2),
3 => _mm_insert_epi32(*vals, val, 3),
_ => unreachable!(),
_ => core::hint::unreachable_unchecked(),
}
};
::std::mem::replace(vals, updated);
unsafe {
::std::ptr::write(vals, updated);
}
}
Loading