如何为 Rust 中的不同线程克隆随机数生成器?
How can I clone a Random Number Generator for different threads in Rust?
我正在研究基于 C++ 代码库(PBRT,如果有人熟悉它)的光线追踪器的 Rust 实现。 C++ 版本定义的 类 之一是一系列采样器,以减少渲染图像中的噪声。在渲染过程中,只要需要随机数,这个采样器就会被克隆到每个渲染线程中。这就是我在 Rust 中选择的方式,我承认这有点令人费解:
#[derive(Clone)]
pub struct PixelSampler {
samples_1d: Vec<Vec<f64>>,
samples_2d: Vec<Vec<Point2<f64>>>,
current_1d_dimension: i32,
current_2d_dimension: i32,
rng: rngs::ThreadRng
}
pub enum Samplers {
StratifiedSampler {x_samples: i64, y_samples: i64, jitter_samples: bool, pixel: PixelSampler },
ZeroTwoSequenceSampler { pixel: PixelSampler }
}
impl Clone for Samplers {
fn clone(&self) -> Self {
match self {
Samplers::StratifiedSampler { x_samples, y_samples, jitter_samples, pixel } => {
Samplers::StratifiedSampler {x_samples: *x_samples,
y_samples: *y_samples,
jitter_samples: *jitter_samples,
pixel: pixel.clone() }
}
Samplers::ZeroTwoSequenceSampler { pixel } => { Samplers::ZeroTwoSequenceSampler{ pixel: pixel.clone() } }
}
}
}
然后我还有一个 Integrator
,它有一个 Samplers
变体字段。在我的主渲染循环中,我为每个线程运行了以下循环:
for _ in 0..NUM_THREADS {
let int_clone = integrator.clone(); // integrator just contains a Samplers
thread_vec.push(thread::spawn(move || {
loop {
// do main rendering loop
}
}));
}
但是当我用这个编译时,我得到了错误:
“特性 std::marker::Send
没有为 std::ptr::NonNull<rand::rngs::adapter::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha20Core, rand_core::os::OsRng>>
实现”。
我的理解是,因为我只是将克隆版本移动到线程中,所以没有必要实现 Send
。我做错了什么?
正如 thread_rng()
文档所说,它是:
[...]essentially just a reference to the PRNG in thread-local memory.
因此,通过克隆“rng”,您并没有复制生成器及其状态(我假设这是您的意图),而是为线程本地 RNG 创建了一个新句柄。此句柄有意不是 Send
,因为它访问线程局部 RNG 而无需锁定以提高效率。
如果您希望您的结构包含一个实际的 RNG,并克隆该结构以复制它,您可以使用 StdRng
type, which is the recommended, efficient and secure RNG. To instantiate it, use the methods in the SeedableRng
特性。例如:
#[derive(Clone)]
pub struct PixelSampler {
samples_1d: Vec<Vec<f64>>,
samples_2d: Vec<Vec<Point2<f64>>>,
current_1d_dimension: i32,
current_2d_dimension: i32,
rng: StdRng,
}
// ...
let sampler = Samplers::ZeroTwoSequenceSampler {
pixel: PixelSampler {
samples_1d: vec![],
samples_2d: vec![],
current_1d_dimension: 0,
current_2d_dimension: 0,
rng: SeedableRng::from_entropy(),
},
};
// compiles because StdRng is Send
std::thread::spawn(move || sampler).join().unwrap();
playground 中的完整示例。
我正在研究基于 C++ 代码库(PBRT,如果有人熟悉它)的光线追踪器的 Rust 实现。 C++ 版本定义的 类 之一是一系列采样器,以减少渲染图像中的噪声。在渲染过程中,只要需要随机数,这个采样器就会被克隆到每个渲染线程中。这就是我在 Rust 中选择的方式,我承认这有点令人费解:
#[derive(Clone)]
pub struct PixelSampler {
samples_1d: Vec<Vec<f64>>,
samples_2d: Vec<Vec<Point2<f64>>>,
current_1d_dimension: i32,
current_2d_dimension: i32,
rng: rngs::ThreadRng
}
pub enum Samplers {
StratifiedSampler {x_samples: i64, y_samples: i64, jitter_samples: bool, pixel: PixelSampler },
ZeroTwoSequenceSampler { pixel: PixelSampler }
}
impl Clone for Samplers {
fn clone(&self) -> Self {
match self {
Samplers::StratifiedSampler { x_samples, y_samples, jitter_samples, pixel } => {
Samplers::StratifiedSampler {x_samples: *x_samples,
y_samples: *y_samples,
jitter_samples: *jitter_samples,
pixel: pixel.clone() }
}
Samplers::ZeroTwoSequenceSampler { pixel } => { Samplers::ZeroTwoSequenceSampler{ pixel: pixel.clone() } }
}
}
}
然后我还有一个 Integrator
,它有一个 Samplers
变体字段。在我的主渲染循环中,我为每个线程运行了以下循环:
for _ in 0..NUM_THREADS {
let int_clone = integrator.clone(); // integrator just contains a Samplers
thread_vec.push(thread::spawn(move || {
loop {
// do main rendering loop
}
}));
}
但是当我用这个编译时,我得到了错误:
“特性 std::marker::Send
没有为 std::ptr::NonNull<rand::rngs::adapter::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha20Core, rand_core::os::OsRng>>
实现”。
我的理解是,因为我只是将克隆版本移动到线程中,所以没有必要实现 Send
。我做错了什么?
正如 thread_rng()
文档所说,它是:
[...]essentially just a reference to the PRNG in thread-local memory.
因此,通过克隆“rng”,您并没有复制生成器及其状态(我假设这是您的意图),而是为线程本地 RNG 创建了一个新句柄。此句柄有意不是 Send
,因为它访问线程局部 RNG 而无需锁定以提高效率。
如果您希望您的结构包含一个实际的 RNG,并克隆该结构以复制它,您可以使用 StdRng
type, which is the recommended, efficient and secure RNG. To instantiate it, use the methods in the SeedableRng
特性。例如:
#[derive(Clone)]
pub struct PixelSampler {
samples_1d: Vec<Vec<f64>>,
samples_2d: Vec<Vec<Point2<f64>>>,
current_1d_dimension: i32,
current_2d_dimension: i32,
rng: StdRng,
}
// ...
let sampler = Samplers::ZeroTwoSequenceSampler {
pixel: PixelSampler {
samples_1d: vec![],
samples_2d: vec![],
current_1d_dimension: 0,
current_2d_dimension: 0,
rng: SeedableRng::from_entropy(),
},
};
// compiles because StdRng is Send
std::thread::spawn(move || sampler).join().unwrap();
playground 中的完整示例。