There seems to be some significant overhead in index-checking, too. I’ve played around with the code and made it more idiomatic (using iterators, in particular zip
and sum
, and no more explicit indexing of individual items), which brings it not all the way to the C++, but a bit closer. I suppose the rest might be because of the different RNG implementation.
diff --git a/main.rs b/main.rs
index 4520871..756eb0b 100644
--- a/main.rs
+++ b/main.rs
@@ -1,35 +1,25 @@
use rand::{distributions::Uniform, Rng};
-use rand::{SeedableRng, rngs::StdRng};
+use rand::{rngs::StdRng, SeedableRng};
use std::time::Instant;
struct WaveFunction {
- num_particles: usize,
- dimensions: usize,
alpha: f64,
}
impl WaveFunction {
- pub fn new(num_particles: usize, dimensions: usize, alpha: f64) -> WaveFunction {
+ pub fn new(alpha: f64) -> WaveFunction {
Self {
- num_particles,
- dimensions,
- alpha
+ alpha,
}
}
- fn compute_r_squared(&self, particles: &Vec<f64>) -> f64 {
- let mut r_squared = 0.;
-
- for i in 0..self.num_particles*self.dimensions {
- r_squared += particles[i] * particles[i];
- }
-
- r_squared
+ fn compute_r_squared(&self, particles: &[f64]) -> f64 {
+ particles.iter().map(|&p| p*p).sum()
}
- pub fn evaluate(&self, particles: &Vec<f64>) -> f64 {
- (- self.alpha * self.compute_r_squared(particles)).exp()
+ pub fn evaluate(&self, particles: &[f64]) -> f64 {
+ (-self.alpha * self.compute_r_squared(particles)).exp()
}
}
@@ -43,11 +33,16 @@ struct System {
}
impl System {
- pub fn new(num_particles: usize, dimensions: usize, alpha: f64, delta_t: f64, seed: u64) -> System {
- let rng: StdRng = SeedableRng::seed_from_u64(seed);
-
- let particles = rng
- .clone()
+ pub fn new(
+ num_particles: usize,
+ dimensions: usize,
+ alpha: f64,
+ delta_t: f64,
+ seed: u64,
+ ) -> System {
+ let mut rng: StdRng = SeedableRng::seed_from_u64(seed);
+
+ let particles = (&mut rng)
.sample_iter(&Uniform::<f64>::new(-1., 1.))
.take(num_particles * dimensions)
.collect();
@@ -57,7 +52,7 @@ impl System {
particles,
num_particles,
dimensions,
- wave_function: WaveFunction::new(num_particles, dimensions, alpha),
+ wave_function: WaveFunction::new(alpha),
delta_t,
}
}
@@ -72,15 +67,18 @@ impl System {
fn metropolis_step(&mut self) -> bool {
let particle = self.rng.gen_range(0..self.num_particles);
- let movement: Vec<f64> = self.rng.clone()
+ let movement: Vec<f64> = (&mut self.rng)
.sample_iter(&Uniform::<f64>::new(-self.delta_t, self.delta_t))
.take(self.dimensions)
.collect();
let wave_before = self.wave_function.evaluate(&self.particles);
- for dim in 0..self.dimensions {
- self.particles[particle * self.dimensions + dim] += movement[dim];
+ for (p, m) in self.particles[particle * self.dimensions..]
+ .iter_mut()
+ .zip(&movement)
+ {
+ *p += *m;
}
let wave_after = self.wave_function.evaluate(&self.particles);
@@ -88,11 +86,14 @@ impl System {
let ratio = (wave_after * wave_after) / (wave_before * wave_before);
if self.rng.gen::<f64>() < ratio {
- return true
+ return true;
}
- for dim in 0..self.dimensions {
- self.particles[particle * self.dimensions + dim] -= movement[dim];
+ for (p, m) in self.particles[particle * self.dimensions..]
+ .iter_mut()
+ .zip(&movement)
+ {
+ *p -= *m;
}
false
$ cargo run --release
Compiling benchmark v0.1.0 (/home/frank/forks/just-a-clone/tmp/2283842)
Finished release [optimized] target(s) in 0.49s
Running `target/release/benchmark`
Accepted: 809759
115
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/benchmark`
Accepted: 809759
110
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/benchmark`
Accepted: 809759
110
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/benchmark`
Accepted: 809759
110
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/benchmark`
Accepted: 809759
109
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/benchmark`
Accepted: 809759
110
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/benchmark`
Accepted: 809759
110
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/benchmark`
Accepted: 809759
110
$ ./a.out
809554
Time: 95
$ ./a.out
809554
Time: 102
$ ./a.out
809554
Time: 100
$ ./a.out
809554
Time: 97
$ ./a.out
809554
Time: 97
$ ./a.out
809554
Time: 102
$ ./a.out
809554
Time: 101
$ ./a.out
809554
Time: 100
(so roughly 90% the speed now, though they also started out a bit closer together on my machine)