Skip to content

Commit

Permalink
test: use full range for floats uniform (#60)
Browse files Browse the repository at this point in the history
* test: use full range for floats uniform

* 🧹 formatting

* tests: use SampleUniformFullRange in integration tests

* bench: update benchmarks with SampleUniformFullRange

* test: update int and uint with SampleUniformFullRange

* fix: use correct trait

* ci: skip MIPS as it is not supported on nightly anymore

* 🧹 remove unused function

* fix: use trait instead of removed function

* :detetctive: float uniform test

* ⬆️ update dev dependencies

* 🧹 fix arrow2 tests for half

* ⬆️ update codspeed CI action
  • Loading branch information
jvdd authored Jan 17, 2024
1 parent 8d730ac commit c4a4c3c
Show file tree
Hide file tree
Showing 35 changed files with 167 additions and 118 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,12 @@ jobs:
- i586-unknown-linux-gnu
- aarch64-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf
- mips-unknown-linux-gnu
- mips64-unknown-linux-gnuabi64
# MIPS is currently not supported anymore on nightly chains.
# more information:
# - https://github.com/rust-lang/compiler-team/issues/648
# - https://github.com/rust-lang/rust/pull/113274
# - mips-unknown-linux-gnu
# - mips64-unknown-linux-gnuabi64
- powerpc-unknown-linux-gnu
- powerpc64-unknown-linux-gnu
- riscv64gc-unknown-linux-gnu
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codspeed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Build the benchmark target(s)
run: cargo codspeed build --features half

- uses: CodSpeedHQ/action@v1
- uses: CodSpeedHQ/action@v2
name: Run benchmarks
with:
run: cargo codspeed run
17 changes: 10 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ exclude = [".git*", "dev_utils/**/*", "tests/**/*"]


[dependencies]
num-traits = { version = "0.2.15", default-features = false }
half = { version = "2.1.0", default-features = false, features=["num-traits"], optional = true }
num-traits = { version = "0.2.17", default-features = false }
half = { version = "2.3.1", default-features = false, features=["num-traits"], optional = true }
ndarray = { version = "0.15.6", default-features = false, optional = true}
arrow = { version = ">0", default-features = false, optional = true}
arrow2 = { version = ">0.0", default-features = false, optional = true}
Expand All @@ -31,13 +31,16 @@ arrow = ["dep:arrow"]
arrow2 = ["dep:arrow2"]

[dev-dependencies]
rstest = { version = "0.16", default-features = false }
rstest_reuse = { version = "0.5", default-features = false }
rand = { version = "0.7.2", default-features = false }
codspeed-criterion-compat = "1.1"
criterion = "0.3.1"
rstest = { version = "0.18.2", default-features = false }
rstest_reuse = { version = "0.6", default-features = false }
rand = { version = "0.8.5", default-features = false }
codspeed-criterion-compat = "2.3.3"
criterion = "0.5.1"
dev_utils = { path = "dev_utils" }

[dev-dependencies.half]
features = ["rand_distr"]


[[bench]]
name = "bench_f16_return_nan"
Expand Down
19 changes: 1 addition & 18 deletions benches/bench_f16_ignore_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,11 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

use half::f16;

fn get_random_f16_array(n: usize) -> Vec<f16> {
let data = utils::get_random_array::<i16>(n, -0x7C00, 0x7C00);
let data: Vec<f16> = data.iter().map(|&x| f16::from_bits(x as u16)).collect();
// Replace NaNs and Infs with 0
let data: Vec<f16> = data
.iter()
.map(|&x| {
if x.is_nan() || x.is_infinite() {
f16::from_bits(0)
} else {
x
}
})
.collect();
data
}

// _in stands for "ignore nan"

fn argminmax_in_f16_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[f16] = &get_random_f16_array(n);
let data: &[f16] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_f16_argminmax_in", |b| {
b.iter(|| SCALAR::<FloatIgnoreNaN>::argminmax(black_box(data)))
});
Expand Down
19 changes: 1 addition & 18 deletions benches/bench_f16_return_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,11 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

use half::f16;

fn get_random_f16_array(n: usize) -> Vec<f16> {
let data = utils::get_random_array::<i16>(n, -0x7C00, 0x7C00);
let data: Vec<f16> = data.iter().map(|&x| f16::from_bits(x as u16)).collect();
// Replace NaNs and Infs with 0
let data: Vec<f16> = data
.iter()
.map(|&x| {
if x.is_nan() || x.is_infinite() {
f16::from_bits(0)
} else {
x
}
})
.collect();
data
}

// _rn stands for "return nan"

fn argminmax_rn_f16_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[f16] = &get_random_f16_array(n);
let data: &[f16] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_f16_argminmax_rn", |b| {
b.iter(|| SCALAR::<FloatReturnNaN>::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_f32_ignore_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_in_f32_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[f32] = &utils::get_random_array::<f32>(n, f32::MIN, f32::MAX);
let data: &[f32] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_f32_argminmax_in", |b| {
b.iter(|| SCALAR::<FloatIgnoreNaN>::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_f32_return_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_rn_f32_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[f32] = &utils::get_random_array::<f32>(n, f32::MIN, f32::MAX);
let data: &[f32] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_f32_argminmax_rn", |b| {
b.iter(|| SCALAR::<FloatReturnNaN>::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_f64_ignore_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_in_f64_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[f64] = &utils::get_random_array::<f64>(n, f64::MIN, f64::MAX);
let data: &[f64] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_f64_argminmax_in", |b| {
b.iter(|| SCALAR::<FloatIgnoreNaN>::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_f64_return_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_rn_f64_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[f64] = &utils::get_random_array::<f64>(n, f64::MIN, f64::MAX);
let data: &[f64] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_f64_argminmax_rn", |b| {
b.iter(|| SCALAR::<FloatReturnNaN>::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_i16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_i16_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[i16] = &utils::get_random_array::<i16>(n, i16::MIN, i16::MAX);
let data: &[i16] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_i16_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_i32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_i32_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[i32] = &utils::get_random_array::<i32>(n, i32::MIN, i32::MAX);
let data: &[i32] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_i32_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_i64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_i64_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[i64] = &utils::get_random_array::<i64>(n, i64::MIN, i64::MAX);
let data: &[i64] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_i64_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_i8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_i8_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[i8] = &utils::get_random_array::<i8>(n, i8::MIN, i8::MAX);
let data: &[i8] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_i8_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_u16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_u16_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[u16] = &utils::get_random_array::<u16>(n, u16::MIN, u16::MAX);
let data: &[u16] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_u16_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_u32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_u32_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[u32] = &utils::get_random_array::<u32>(n, u32::MIN, u32::MAX);
let data: &[u32] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_u32_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_u64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_u64_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[u64] = &utils::get_random_array::<u64>(n, u64::MIN, u64::MAX);
let data: &[u64] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_u64_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
2 changes: 1 addition & 1 deletion benches/bench_u8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use argminmax::simd::{SIMDArgMinMax, NEON};

fn argminmax_u8_random_array_long(c: &mut Criterion) {
let n = config::ARRAY_LENGTH_LONG;
let data: &[u8] = &utils::get_random_array::<u8>(n, u8::MIN, u8::MAX);
let data: &[u8] = &utils::SampleUniformFullRange::get_random_array(n);
c.bench_function("scalar_u8_argminmax", |b| {
b.iter(|| SCALAR::argminmax(black_box(data)))
});
Expand Down
5 changes: 3 additions & 2 deletions dev_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ edition = "2021"
description = "Shared utilities for development (tests & benchmarks)"

[dependencies]
rand = { version = "0.7.2", default-features = false }
rand_distr = { version = "0.2.2", default-features = false }
num-traits = { version = "0.2.17", default-features = false }
half = { version = "2.3.1", default-features = false, features = ["num-traits", "rand_distr"] }
rand = { version = "0.8.5" }
72 changes: 61 additions & 11 deletions dev_utils/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
use half::f16;
use num_traits::Zero;

use std::ops::{Add, Sub};

use rand::distributions::Uniform;
use rand::{thread_rng, Rng};
use rand_distr::Uniform;

// random array that samples between min and max of T
pub fn get_random_array<T>(n: usize, min_value: T, max_value: T) -> Vec<T>
where
T: Copy + rand::distributions::uniform::SampleUniform,
{
let rng = thread_rng();
let uni = Uniform::new_inclusive(min_value, max_value);
rng.sample_iter(uni).take(n).collect()
}

// worst case array that alternates between increasing max and decreasing min values
pub fn get_worst_case_array<T>(n: usize, step: T) -> Vec<T>
Expand All @@ -32,3 +25,60 @@ where
}
arr
}

pub trait SampleUniformFullRange: rand::distributions::uniform::SampleUniform {
const MIN: Self;
const MAX: Self;

// random array that samples between min and max of Self
fn get_random_array(n: usize) -> Vec<Self>
where
Self: Copy + rand::distributions::uniform::SampleUniform,
{
let rng = thread_rng();
let uni = Uniform::new_inclusive(Self::MIN, Self::MAX);
rng.sample_iter(uni).take(n).collect()
}
}

macro_rules! impl_full_range_uniform {
($($t:ty),*) => {
$(
impl SampleUniformFullRange for $t {
const MIN: Self = <$t>::MIN;
const MAX: Self = <$t>::MAX;
}
)*
};
}

macro_rules! impl_full_range_uniform_float {
($($t:ty, $t_int:ty),*) => {
$(
impl SampleUniformFullRange for $t {
// These 2 are not used, but are required by the trait
const MIN: Self = <$t>::MIN;
const MAX: Self = <$t>::MAX;

fn get_random_array(n: usize) -> Vec<Self>
where
Self: Copy + rand::distributions::uniform::SampleUniform,
{
// Get a uniform random array of integers
let rand_arr_int: Vec<$t_int> = <$t_int>::get_random_array(n);
// Transmute the integers to floats
let rand_arr_float: Vec<Self> = unsafe { std::mem::transmute(rand_arr_int) };
// Replace the NaNs with 0.0
rand_arr_float.iter().map(|x| if x.is_nan() { <$t>::zero() } else { *x }).collect()
}
}
)*
};
}

impl_full_range_uniform!(i8, i16, i32, i64, u8, u16, u32, u64);
// f16 does not suffer from range overflow panick as upcast to f32 is used to generate
// the random numbers
impl_full_range_uniform!(f16);
// Workaround for f32 and f64 as these suffer from range overflow in the rand crate
impl_full_range_uniform_float!(f32, i32, f64, i64);
6 changes: 2 additions & 4 deletions src/scalar/scalar_f16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,8 @@ mod tests {
const ARR_LEN: usize = 1025;

fn get_arrays(len: usize) -> (Vec<f32>, Vec<f16>) {
// we use i8 its to make sure we have correct representation in float
let v = utils::get_random_array(len, i8::MIN, i8::MAX);
let vec_f32: Vec<f32> = v.iter().map(|x| *x as f32).collect();
let vec_f16: Vec<f16> = vec_f32.iter().map(|x| f16::from_f32(*x)).collect();
let vec_f16: Vec<f16> = utils::SampleUniformFullRange::get_random_array(len);
let vec_f32: Vec<f32> = vec_f16.iter().map(|x| x.to_f32()).collect();
(vec_f32, vec_f16)
}

Expand Down
3 changes: 1 addition & 2 deletions src/simd/simd_f16_ignore_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,8 +726,7 @@ mod tests {
use dev_utils::utils;

fn get_array_f16(n: usize) -> Vec<f16> {
let arr = utils::get_random_array(n, i16::MIN, i16::MAX);
arr.iter().map(|x| f16::from_f32(*x as f32)).collect()
utils::SampleUniformFullRange::get_random_array(n)
}

// The scalar implementation
Expand Down
3 changes: 1 addition & 2 deletions src/simd/simd_f16_return_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,8 +687,7 @@ mod tests {
use dev_utils::utils;

fn get_array_f16(n: usize) -> Vec<f16> {
let arr = utils::get_random_array(n, i16::MIN, i16::MAX);
arr.iter().map(|x| f16::from_f32(*x as f32)).collect()
utils::SampleUniformFullRange::get_random_array(n)
}

// The scalar implementation
Expand Down
2 changes: 1 addition & 1 deletion src/simd/simd_f32_ignore_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ mod tests {
use dev_utils::utils;

fn get_array_f32(n: usize) -> Vec<f32> {
utils::get_random_array(n, f32::MIN, f32::MAX)
utils::SampleUniformFullRange::get_random_array(n)
}

// The scalar implementation
Expand Down
2 changes: 1 addition & 1 deletion src/simd/simd_f32_return_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ mod tests {
use dev_utils::utils;

fn get_array_f32(n: usize) -> Vec<f32> {
utils::get_random_array(n, f32::MIN, f32::MAX)
utils::SampleUniformFullRange::get_random_array(n)
}

// The scalar implementation
Expand Down
2 changes: 1 addition & 1 deletion src/simd/simd_f64_ignore_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ mod tests {
use dev_utils::utils;

fn get_array_f64(n: usize) -> Vec<f64> {
utils::get_random_array(n, f64::MIN, f64::MAX)
utils::SampleUniformFullRange::get_random_array(n)
}

// The scalar implementation
Expand Down
2 changes: 1 addition & 1 deletion src/simd/simd_f64_return_nan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ mod tests {
use dev_utils::utils;

fn get_array_f64(n: usize) -> Vec<f64> {
utils::get_random_array(n, f64::MIN, f64::MAX)
utils::SampleUniformFullRange::get_random_array(n)
}

// The scalar implementation
Expand Down
2 changes: 1 addition & 1 deletion src/simd/simd_i16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ mod tests {
use dev_utils::utils;

fn get_array_i16(n: usize) -> Vec<i16> {
utils::get_random_array(n, i16::MIN, i16::MAX)
utils::SampleUniformFullRange::get_random_array(n)
}

// The scalar implementation
Expand Down
Loading

0 comments on commit c4a4c3c

Please sign in to comment.