Skip to content

Commit a494e2b

Browse files
authored
[byteorder] Optimize tests; large impact on Miri (#350)
While they take a trivial amount of time running natively, the `test_native_endian` and `test_non_native_endian` unit tests previously took a long time to execute on Miri, slowing down local development and CI. As of the writing of this commit message, I was experiencing execution times for each test of ~60s. This commit optimizes those tests in two ways: - Use a faster RNG - When running under Miri (detected using `cfg!(miri)`), perform a single loop iteration rather than 1024 Using `time cargo miri test -- --skip ui`, I have benchmarked the execution time before this commit at ~2m36s and the time after this commit at ~8s.
1 parent e7a36dc commit a494e2b

File tree

2 files changed

+52
-11
lines changed

2 files changed

+52
-11
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ optional = true
6969
zerocopy-derive = { version = "=0.7.3", path = "zerocopy-derive" }
7070

7171
[dev-dependencies]
72-
rand = "0.6"
72+
rand = { version = "0.8.5", features = ["small_rng"] }
7373
rustversion = "1.0"
7474
static_assertions = "1.1"
7575
# Pinned to a specific version so that the version used for local development

src/byteorder.rs

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,11 @@ module!(native_endian, NativeEndian, "native-endian");
496496
#[cfg(test)]
497497
mod tests {
498498
use ::byteorder::NativeEndian;
499+
use rand::{
500+
distributions::{Distribution, Standard},
501+
rngs::SmallRng,
502+
Rng, SeedableRng,
503+
};
499504

500505
use {
501506
super::*,
@@ -507,7 +512,12 @@ mod tests {
507512
const ZERO: Self;
508513
const MAX_VALUE: Self;
509514

510-
fn rand() -> Self;
515+
type Distribution: Distribution<Self>;
516+
const DIST: Self::Distribution;
517+
518+
fn rand<R: Rng>(rng: &mut R) -> Self {
519+
rng.sample(Self::DIST)
520+
}
511521
}
512522

513523
trait ByteArray:
@@ -570,9 +580,8 @@ mod tests {
570580
const ZERO: $native = 0 as $native;
571581
const MAX_VALUE: $native = $native::MAX;
572582

573-
fn rand() -> $native {
574-
rand::random()
575-
}
583+
type Distribution = Standard;
584+
const DIST: Standard = Standard;
576585
}
577586

578587
impl<O: ByteOrder> ByteOrderType for $name<O> {
@@ -646,6 +655,36 @@ mod tests {
646655
#[cfg(target_endian = "little")]
647656
type NonNativeEndian = BigEndian;
648657

658+
// We use a `u64` seed so that we can use `SeedableRng::seed_from_u64`.
659+
// `SmallRng`'s `SeedableRng::Seed` differs by platform, so if we wanted to
660+
// call `SeedableRng::from_seed`, which takes a `Seed`, we would need
661+
// conditional compilation by `target_pointer_width`.
662+
const RNG_SEED: u64 = 0x7A03CAE2F32B5B8F;
663+
664+
const RAND_ITERS: usize = if cfg!(miri) {
665+
// The tests below which use this constant used to take a very long time
666+
// on Miri, which slows down local development and CI jobs. We're not
667+
// using Miri to check for the correctness of our code, but rather its
668+
// soundness, and at least in the context of these particular tests, a
669+
// single loop iteration is just as good for surfacing UB as multiple
670+
// iterations are.
671+
//
672+
// As of the writing of this comment, here's one set of measurements:
673+
//
674+
// $ # RAND_ITERS == 1
675+
// $ cargo miri test -- -Z unstable-options --report-time endian
676+
// test byteorder::tests::test_native_endian ... ok <0.049s>
677+
// test byteorder::tests::test_non_native_endian ... ok <0.061s>
678+
//
679+
// $ # RAND_ITERS == 1024
680+
// $ cargo miri test -- -Z unstable-options --report-time endian
681+
// test byteorder::tests::test_native_endian ... ok <25.716s>
682+
// test byteorder::tests::test_non_native_endian ... ok <38.127s>
683+
1
684+
} else {
685+
1024
686+
};
687+
649688
#[test]
650689
fn test_zero() {
651690
fn test_zero<T: ByteOrderType>() {
@@ -669,8 +708,9 @@ mod tests {
669708
#[test]
670709
fn test_native_endian() {
671710
fn test_native_endian<T: ByteOrderType>() {
672-
for _ in 0..1024 {
673-
let native = T::Native::rand();
711+
let mut r = SmallRng::seed_from_u64(RNG_SEED);
712+
for _ in 0..RAND_ITERS {
713+
let native = T::Native::rand(&mut r);
674714
let mut bytes = T::ByteArray::default();
675715
bytes.as_bytes_mut().copy_from_slice(native.as_bytes());
676716
let mut from_native = T::new(native);
@@ -681,7 +721,7 @@ mod tests {
681721
assert_eq!(from_native.into_bytes(), bytes);
682722
assert_eq!(from_bytes.into_bytes(), bytes);
683723

684-
let updated = T::Native::rand();
724+
let updated = T::Native::rand(&mut r);
685725
from_native.set(updated);
686726
assert_eq!(from_native.get(), updated);
687727
}
@@ -693,8 +733,9 @@ mod tests {
693733
#[test]
694734
fn test_non_native_endian() {
695735
fn test_non_native_endian<T: ByteOrderType>() {
696-
for _ in 0..1024 {
697-
let native = T::Native::rand();
736+
let mut r = SmallRng::seed_from_u64(RNG_SEED);
737+
for _ in 0..RAND_ITERS {
738+
let native = T::Native::rand(&mut r);
698739
let mut bytes = T::ByteArray::default();
699740
bytes.as_bytes_mut().copy_from_slice(native.as_bytes());
700741
bytes = bytes.invert();
@@ -706,7 +747,7 @@ mod tests {
706747
assert_eq!(from_native.into_bytes(), bytes);
707748
assert_eq!(from_bytes.into_bytes(), bytes);
708749

709-
let updated = T::Native::rand();
750+
let updated = T::Native::rand(&mut r);
710751
from_native.set(updated);
711752
assert_eq!(from_native.get(), updated);
712753
}

0 commit comments

Comments
 (0)