Change hand-picked RNGs back to SmallRng

While `Xoshiro256+` is faster on 64-bit, it has low linear complexity in the
lower three bits, which *are* used when generating dither.

Also, while `Xoshiro128StarStar` access one less variable from the heap,
multiplication is generally slower than addition in hardware.
This commit is contained in:
Roderick van Domburg 2021-10-21 19:31:58 +02:00
parent 4c89a721ee
commit ff3648434b
No known key found for this signature in database
GPG key ID: FE2585E713F9F30A
3 changed files with 7 additions and 34 deletions

10
Cargo.lock generated
View file

@ -1288,7 +1288,6 @@ dependencies = [
"portaudio-rs", "portaudio-rs",
"rand", "rand",
"rand_distr", "rand_distr",
"rand_xoshiro",
"rodio", "rodio",
"sdl2", "sdl2",
"shell-words", "shell-words",
@ -1899,15 +1898,6 @@ dependencies = [
"rand_core", "rand_core",
] ]
[[package]]
name = "rand_xoshiro"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.10" version = "0.2.10"

View file

@ -47,9 +47,8 @@ lewton = "0.10"
ogg = "0.8" ogg = "0.8"
# Dithering # Dithering
rand = "0.8" rand = { version = "0.8", features = ["small_rng"] }
rand_distr = "0.4" rand_distr = "0.4"
rand_xoshiro = "0.6"
[features] [features]
alsa-backend = ["alsa"] alsa-backend = ["alsa"]

View file

@ -1,3 +1,4 @@
use rand::rngs::SmallRng;
use rand::SeedableRng; use rand::SeedableRng;
use rand_distr::{Distribution, Normal, Triangular, Uniform}; use rand_distr::{Distribution, Normal, Triangular, Uniform};
use std::fmt; use std::fmt;
@ -41,29 +42,12 @@ impl fmt::Display for dyn Ditherer {
} }
} }
// `SmallRng` is 33% faster than `ThreadRng`, but we can do even better. fn create_rng() -> SmallRng {
// `SmallRng` defaults to `Xoshiro256PlusPlus` on 64-bit platforms and SmallRng::from_entropy()
// `Xoshiro128PlusPlus` on 32-bit platforms. These are excellent for the
// general case. In our case of just 64-bit floating points, we can make
// some optimizations. Compared to `SmallRng`, these hand-picked generators
// improve performance by another 9% on 64-bit platforms and 2% on 32-bit
// platforms.
//
// For reference, see https://prng.di.unimi.it. Note that we do not use
// `Xoroshiro128Plus` or `Xoshiro128Plus` because they display low linear
// complexity in the lower four bits, which is not what we want:
// linearization is the very point of dithering.
#[cfg(target_pointer_width = "64")]
type Rng = rand_xoshiro::Xoshiro256Plus;
#[cfg(not(target_pointer_width = "64"))]
type Rng = rand_xoshiro::Xoshiro128StarStar;
fn create_rng() -> Rng {
Rng::from_entropy()
} }
pub struct TriangularDitherer { pub struct TriangularDitherer {
cached_rng: Rng, cached_rng: SmallRng,
distribution: Triangular<f64>, distribution: Triangular<f64>,
} }
@ -90,7 +74,7 @@ impl TriangularDitherer {
} }
pub struct GaussianDitherer { pub struct GaussianDitherer {
cached_rng: Rng, cached_rng: SmallRng,
distribution: Normal<f64>, distribution: Normal<f64>,
} }
@ -119,7 +103,7 @@ impl GaussianDitherer {
pub struct HighPassDitherer { pub struct HighPassDitherer {
active_channel: usize, active_channel: usize,
previous_noises: [f64; NUM_CHANNELS], previous_noises: [f64; NUM_CHANNELS],
cached_rng: Rng, cached_rng: SmallRng,
distribution: Uniform<f64>, distribution: Uniform<f64>,
} }