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)