From f5dc99f7d3c782062ba2f387327c8aa932a1c551 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Fri, 10 Feb 2023 22:51:38 -0500 Subject: [PATCH] refactor: switch to array-init - Use array-init for the lowest level vector operations. Makes them nicer and less bug-prone, with zero performance impact. - Remove unnecessary/unused ::new() functions on Vector and HeightVector. - Update README with new dependency. - Fix typo in README. Signed-off-by: Stephen Waits --- Cargo.toml | 1 + README.md | 10 +++++++--- src/height_vector.rs | 9 ++------- src/vector.rs | 32 ++++++++++---------------------- 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 15493e4..d1964c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ name = "million-updates" harness = false [dependencies] +array-init = "2.1.0" num-traits = "0.2.15" rand = "0.8.5" serde = {version = "1.0.152", features = ["serde_derive"]} diff --git a/README.md b/README.md index 1eb3d3e..4f7b7ee 100644 --- a/README.md +++ b/README.md @@ -123,9 +123,9 @@ vivaldi-nc = { version = "(version)", features = ["f32"] } ### Examples -The repository includes an example which loads a n-to-n latency sample from -PlanetLab and iterates on `NetworkCoordinate`s until some low-enough mean error -is reached. The output is a JSON array of elements which contain the NC's +The repository includes an example which loads a 490 node N-to-N latency sample +from PlanetLab and iterates on `NetworkCoordinate`s until some low-enough mean +error is reached. The output is a JSON array of elements which contain the NC's position, its height (or stem latency estimate), and its estimation of error (lower is better) that look like this: @@ -188,6 +188,10 @@ This crate depends on: - [`serde_with`](https://crates.io/crates/serde_with): Because the inner `Vector` uses a const generic length, we use this to help derive `Deserialize`. +- [`array-init`](https://crates.io/crates/array-init): Makes the array + operations for the internal `Vector` so much nicer. Once we have + `feature(array_zip)` stabilized, we could use that instead. Until then, this + works well with zero performance impact. ## Design Goals & Alternatives diff --git a/src/height_vector.rs b/src/height_vector.rs index 1e40f96..3a6bb75 100644 --- a/src/height_vector.rs +++ b/src/height_vector.rs @@ -92,11 +92,6 @@ pub struct HeightVector { // impl HeightVector { - /// A new height vector is a random unit vector - pub(crate) fn new() -> Self { - Self::random() - } - /// A new height vector is a random unit vector pub(crate) fn random() -> Self { let mut rng = rand::thread_rng(); @@ -131,7 +126,7 @@ impl HeightVector { ret } else { // if we have a bad vector, generate a new random vector - Self::new() + Self::random() } } @@ -318,7 +313,7 @@ mod tests { #[test] fn test_new() { // new gives us a random unit length vec - let a = HeightVector::<3>::new(); + let a = HeightVector::<3>::random(); assert_approx_eq!(a.len(), 1.0); } diff --git a/src/vector.rs b/src/vector.rs index 5bc1ede..ed645b0 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -3,6 +3,7 @@ //! This is the simplest possible implementation of a vector to support the needs of a //! Vivaldi `HeightVector`. +use array_init::array_init; use num_traits::Float; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -37,11 +38,6 @@ impl Vector where T: Float + Serialize + for<'d> Deserialize<'d>, { - /// Create a new Zero vector. - pub(crate) fn new() -> Self { - Self::default() - } - /// Compute magnitude of vector. pub(crate) fn len(&self) -> T { // use hypot() instead of sqrt() of sum of squares to avoid overflows @@ -90,11 +86,9 @@ where /// Vector+Vector addition. fn add(self, rhs: Self) -> Self::Output { - let mut ret = [T::zero(); N]; //Self::Output::new(); - for i in 0..N { - ret[i] = self[i] + rhs[i]; + Self::Output { + inner: array_init(|i| self[i] + rhs[i]), } - Self::Output::from(ret) } } @@ -106,11 +100,9 @@ where /// Vector-Vector subtraction. fn sub(self, rhs: Self) -> Self::Output { - let mut ret = [T::zero(); N]; //Self::Output::new(); - for i in 0..N { - ret[i] = self[i] - rhs[i]; + Self::Output { + inner: array_init(|i| self[i] - rhs[i]), } - Self::Output::from(ret) } } @@ -122,11 +114,9 @@ where /// Vector*scalar multiplication. fn mul(self, rhs: T) -> Self::Output { - let mut ret = Self::Output::new(); - for i in 0..N { - ret.inner[i] = self.inner[i] * rhs; + Self::Output { + inner: array_init(|i| self[i] * rhs), } - ret } } @@ -138,11 +128,9 @@ where /// Vector/scalar division. fn div(self, rhs: T) -> Self::Output { - let mut ret = Self::Output::new(); - for i in 0..N { - ret.inner[i] = self.inner[i] / rhs; + Self::Output { + inner: array_init(|i| self[i] / rhs), } - ret } } @@ -235,7 +223,7 @@ mod tests { #[test] fn test_default_new() { let a = Vector::::default(); - let b = Vector::::new(); + let b = Vector::::from([1.0; 3]) * 0.0; let c = Vector::::from([0.0; 3]); assert_eq!(a, b); assert_eq!(b, c);