From 4d26e8457a4d86635207b130d903c778cba01716 Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Mon, 26 Jul 2021 08:33:39 +0800 Subject: [PATCH 1/6] Test all stable features. Run Rust audit on dependencies. --- .github/workflows/audit.yml | 14 ++++++++++++++ .github/workflows/ci.yaml | 4 ++-- Cargo.toml | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/audit.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 00000000..118008c6 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,14 @@ +name: Security audit +on: + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2a451fbe..c218c54a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -33,11 +33,11 @@ jobs: - uses: actions-rs/cargo@v1 with: - command: build + command: build --features stable - uses: actions-rs/cargo@v1 with: - command: test + command: test --features stable - uses: actions-rs/cargo@v1 if: ${{ matrix.rust == 'stable' }} diff --git a/Cargo.toml b/Cargo.toml index 57cc7f68..d93a33d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ appveyor = { repository = "bheisler/criterion.rs", id = "4255ads9ctpupcl2" } maintenance = { status = "passively-maintained" } [features] +stable = ["async_futures", "async_smol", "async_tokio", "async_std"] default = ["cargo_bench_support"] # Enable use of the nightly-only test::black_box function to discourage compiler optimizations. From 17729a8d5b5839eef6c1953b26bc7e244daf1e82 Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Mon, 26 Jul 2021 08:36:55 +0800 Subject: [PATCH 2/6] Fix yaml syntax. --- .github/workflows/ci.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c218c54a..357dfa7f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -33,11 +33,13 @@ jobs: - uses: actions-rs/cargo@v1 with: - command: build --features stable + command: build + args: --features stable - uses: actions-rs/cargo@v1 with: - command: test --features stable + command: test + args: --features stable - uses: actions-rs/cargo@v1 if: ${{ matrix.rust == 'stable' }} From 03b7468f86d290e8eabf5daf69370353b2d71e30 Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Mon, 26 Jul 2021 08:42:35 +0800 Subject: [PATCH 3/6] Update dev dependencies. --- Cargo.toml | 89 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 57cc7f68..90629d0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,56 +1,59 @@ [package] -authors = ["Jorge Aparicio ", "Brook Heisler "] -name = "criterion" +authors = [ + "Jorge Aparicio ", + "Brook Heisler ", +] +name = "criterion" version = "0.3.4" edition = "2018" description = "Statistics-driven micro-benchmarking library" -homepage = "https://bheisler.github.io/criterion.rs/book/index.html" -repository = "https://github.com/bheisler/criterion.rs" -readme = "README.md" -keywords = ["criterion", "benchmark"] -categories = ["development-tools::profiling"] -license = "Apache-2.0/MIT" -exclude = ["book/*"] +homepage = "https://bheisler.github.io/criterion.rs/book/index.html" +repository = "https://github.com/bheisler/criterion.rs" +readme = "README.md" +keywords = ["criterion", "benchmark"] +categories = ["development-tools::profiling"] +license = "Apache-2.0/MIT" +exclude = ["book/*"] [dependencies] -lazy_static = "1.4" -criterion-plot = { path="plot", version="0.4.3" } -itertools = "0.10" -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" -serde_cbor = "0.11" -atty = "0.2" -clap = { version = "2.33", default-features = false } -csv = "1.1" -walkdir = "2.3" -tinytemplate = "1.1" -cast = "0.2" -num-traits = { version = "0.2", default-features = false } -oorandom = "11.1" -rayon = "1.3" -regex = { version = "1.3", default-features = false, features = ["std"] } -futures = { version = "0.3", default_features = false, optional = true } -smol = { version = "1.2", default-features = false, optional = true } -tokio = { version = "1.0", default-features = false, features = ["rt"], optional = true } -async-std = { version = "1.9", optional = true } +lazy_static = "1.4" +criterion-plot = { path = "plot", version = "0.4.3" } +itertools = "0.10" +serde = "1.0" +serde_json = "1.0" +serde_derive = "1.0" +serde_cbor = "0.11" +atty = "0.2" +clap = { version = "2.33", default-features = false } +csv = "1.1" +walkdir = "2.3" +tinytemplate = "1.1" +cast = "0.2" +num-traits = { version = "0.2", default-features = false } +oorandom = "11.1" +rayon = "1.3" +regex = { version = "1.3", default-features = false, features = ["std"] } +futures = { version = "0.3", default_features = false, optional = true } +smol = { version = "1.2", default-features = false, optional = true } +tokio = { version = "1.0", default-features = false, features = ["rt"], optional = true } +async-std = { version = "1.9", optional = true } [dependencies.plotters] -version = "^0.3.1" +version = "^0.3.1" default-features = false -features = ["svg_backend", "area_series", "line_series"] +features = ["svg_backend", "area_series", "line_series"] [dev-dependencies] -tempfile = "3.1" -approx = "0.4" -quickcheck = { version = "0.9", default-features = false } -rand = "0.8" -futures = { version = "0.3", default_features = false, features = ["executor"] } +tempfile = "3.2.0" +approx = "0.5.0" +quickcheck = { version = "1.0", default-features = false } +rand = "0.8" +futures = { version = "0.3", default_features = false, features = ["executor"] } [badges] -travis-ci = { repository = "bheisler/criterion.rs" } -appveyor = { repository = "bheisler/criterion.rs", id = "4255ads9ctpupcl2" } +travis-ci = { repository = "bheisler/criterion.rs" } +appveyor = { repository = "bheisler/criterion.rs", id = "4255ads9ctpupcl2" } maintenance = { status = "passively-maintained" } [features] @@ -65,9 +68,9 @@ async = ["futures"] # These features enable built-in support for running async benchmarks on each different async # runtime. async_futures = ["futures/executor", "async"] -async_smol = ["smol", "async"] -async_tokio = ["tokio", "async"] -async_std = ["async-std", "async"] +async_smol = ["smol", "async"] +async_tokio = ["tokio", "async"] +async_std = ["async-std", "async"] # This feature _currently_ does nothing except disable a warning message, but in 0.4.0 it will be # required in order to have Criterion.rs generate its own plots (as opposed to using cargo-criterion) @@ -86,7 +89,7 @@ csv_output = [] exclude = ["cargo-criterion"] [[bench]] -name = "bench_main" +name = "bench_main" harness = false [lib] From d462c192a20c090b88f041d216d3d42203e19d0b Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Mon, 26 Jul 2021 08:50:08 +0800 Subject: [PATCH 4/6] Bump MSRV to 1.46 --- .github/workflows/ci.yaml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 357dfa7f..2c399047 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,7 +17,7 @@ jobs: - stable - beta - nightly - - 1.40.0 # MSRV + - 1.46.0 # MSRV steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index 7d9c3e44..ece257c7 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ For more details, see the [CONTRIBUTING.md file](https://github.com/bheisler/cri Criterion.rs supports the last three stable minor releases of Rust. At time of writing, this means Rust 1.50 or later. Older versions may work, but are not guaranteed. -Currently, the oldest version of Rust believed to work is 1.40. Future versions of Criterion.rs may +Currently, the oldest version of Rust believed to work is 1.46. Future versions of Criterion.rs may break support for such old versions, and this will not be considered a breaking change. If you require Criterion.rs to work on old versions of Rust, you will need to stick to a specific patch version of Criterion.rs. From 11880fe49bcd6f91c047a7b63228fce2998da6f8 Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Mon, 26 Jul 2021 09:02:48 +0800 Subject: [PATCH 5/6] Allow fewer clippy warnings now that we have a newer MSRV. --- src/csv_report.rs | 6 +++--- src/html/mod.rs | 18 +++++++----------- src/lib.rs | 12 ++---------- src/stats/univariate/outliers/tukey.rs | 25 +++++-------------------- 4 files changed, 17 insertions(+), 44 deletions(-) diff --git a/src/csv_report.rs b/src/csv_report.rs index f30b8176..3b744df0 100644 --- a/src/csv_report.rs +++ b/src/csv_report.rs @@ -31,14 +31,14 @@ impl CsvReportWriter { let mut data_scaled: Vec = data.sample_times().as_ref().into(); let unit = formatter.scale_for_machines(&mut data_scaled); let group = id.group_id.as_str(); - let function = id.function_id.as_ref().map(String::as_str); - let value = id.value_str.as_ref().map(String::as_str); + let function = id.function_id.as_deref(); + let value = id.value_str.as_deref(); let (throughput_num, throughput_type) = match id.throughput { Some(Throughput::Bytes(bytes)) => (Some(format!("{}", bytes)), Some("bytes")), Some(Throughput::Elements(elems)) => (Some(format!("{}", elems)), Some("elements")), None => (None, None), }; - let throughput_num = throughput_num.as_ref().map(String::as_str); + let throughput_num = throughput_num.as_deref(); for (count, measured_value) in data.iter_counts().iter().zip(data_scaled.into_iter()) { let row = CsvRow { diff --git a/src/html/mod.rs b/src/html/mod.rs index 11abe7ad..d3606562 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -199,8 +199,8 @@ impl<'a> BenchmarkGroup<'a> { let mut individual_links = HashMap::with_capacity(ids.len()); for id in ids.iter() { - let function_id = id.function_id.as_ref().map(String::as_str); - let value = id.value_str.as_ref().map(String::as_str); + let function_id = id.function_id.as_deref(); + let value = id.value_str.as_deref(); let individual_link = ReportLink::individual(output_directory, id); @@ -497,17 +497,13 @@ impl Report for Html { // First sort the ids/data by value. // If all of the value strings can be parsed into a number, sort/dedupe // numerically. Otherwise sort lexicographically. - let all_values_numeric = all_data.iter().all(|(id, _)| { - id.value_str - .as_ref() - .map(String::as_str) - .and_then(try_parse) - .is_some() - }); + let all_values_numeric = all_data + .iter() + .all(|(id, _)| id.value_str.as_deref().and_then(try_parse).is_some()); if all_values_numeric { all_data.sort_unstable_by(|(a, _), (b, _)| { - let num1 = a.value_str.as_ref().map(String::as_str).and_then(try_parse); - let num2 = b.value_str.as_ref().map(String::as_str).and_then(try_parse); + let num1 = a.value_str.as_deref().and_then(try_parse); + let num2 = b.value_str.as_deref().and_then(try_parse); num1.partial_cmp(&num2).unwrap_or(Ordering::Less) }); diff --git a/src/lib.rs b/src/lib.rs index 7cc40706..98dcf1e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,9 +23,7 @@ allow( clippy::just_underscores_and_digits, // Used in the stats code clippy::transmute_ptr_to_ptr, // Used in the stats code - clippy::option_as_ref_deref, // Remove when MSRV bumped above 1.40 clippy::manual_non_exhaustive, // Remove when MSRV bumped above 1.40 - clippy::match_like_matches_macro, // Remove when MSRV bumped above 1.42 ) )] @@ -339,10 +337,7 @@ pub(crate) enum Mode { } impl Mode { pub fn is_benchmark(&self) -> bool { - match self { - Mode::Benchmark => true, - _ => false, - } + matches!(self, Mode::Benchmark) } } @@ -1515,10 +1510,7 @@ impl ActualSamplingMode { } fn is_linear(&self) -> bool { - match self { - ActualSamplingMode::Linear => true, - _ => false, - } + matches!(self, ActualSamplingMode::Linear) } fn recommend_linear_sample_size(target_time: f64, met: f64) -> u64 { diff --git a/src/stats/univariate/outliers/tukey.rs b/src/stats/univariate/outliers/tukey.rs index bfd08f1d..70713ac5 100644 --- a/src/stats/univariate/outliers/tukey.rs +++ b/src/stats/univariate/outliers/tukey.rs @@ -224,42 +224,27 @@ pub enum Label { impl Label { /// Checks if the data point has an "unusually" high value pub fn is_high(&self) -> bool { - match *self { - HighMild | HighSevere => true, - _ => false, - } + matches!(*self, HighMild | HighSevere) } /// Checks if the data point is labeled as a "mild" outlier pub fn is_mild(&self) -> bool { - match *self { - HighMild | LowMild => true, - _ => false, - } + matches!(*self, HighMild | LowMild) } /// Checks if the data point has an "unusually" low value pub fn is_low(&self) -> bool { - match *self { - LowMild | LowSevere => true, - _ => false, - } + matches!(*self, LowMild | LowSevere) } /// Checks if the data point is labeled as an outlier pub fn is_outlier(&self) -> bool { - match *self { - NotAnOutlier => false, - _ => true, - } + matches!(*self, NotAnOutlier) } /// Checks if the data point is labeled as a "severe" outlier pub fn is_severe(&self) -> bool { - match *self { - HighSevere | LowSevere => true, - _ => false, - } + matches!(*self, HighSevere | LowSevere) } } From 54db222a4f3f157ffee4b07dbb5adbebe4c2d772 Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Mon, 26 Jul 2021 09:45:41 +0800 Subject: [PATCH 6/6] Limit testing to values that fit within a u8. --- src/stats/bivariate/bootstrap.rs | 16 +++++++++++---- src/stats/univariate/bootstrap.rs | 32 ++++++++++++++++++++++-------- src/stats/univariate/kde/kernel.rs | 4 +++- src/stats/univariate/kde/mod.rs | 4 +++- src/stats/univariate/resamples.rs | 4 +++- 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/stats/bivariate/bootstrap.rs b/src/stats/bivariate/bootstrap.rs index 8fe8edec..9eb7fa7b 100644 --- a/src/stats/bivariate/bootstrap.rs +++ b/src/stats/bivariate/bootstrap.rs @@ -10,8 +10,12 @@ macro_rules! test { use crate::stats::bivariate::Data; quickcheck! { - fn means(size: usize, start: usize, - offset: usize, nresamples: usize) -> TestResult { + fn means(size: u8, start: u8, + offset: u8, nresamples: u8) -> TestResult { + let size = size as usize; + let start = start as usize; + let offset = offset as usize; + let nresamples = nresamples as usize; if let Some(x) = crate::stats::test::vec::<$ty>(size, start) { let y = crate::stats::test::vec::<$ty>(size + offset, start + offset).unwrap(); let data = Data::new(&x[start..], &y[start+offset..]); @@ -48,8 +52,12 @@ macro_rules! test { } quickcheck! { - fn slope(size: usize, start: usize, - offset: usize, nresamples: usize) -> TestResult { + fn slope(size: u8, start: u8, + offset: u8, nresamples: u8) -> TestResult { + let size = size as usize; + let start = start as usize; + let offset = offset as usize; + let nresamples = nresamples as usize; if let Some(x) = crate::stats::test::vec::<$ty>(size, start) { let y = crate::stats::test::vec::<$ty>(size + offset, start + offset).unwrap(); let data = Data::new(&x[start..], &y[start+offset..]); diff --git a/src/stats/univariate/bootstrap.rs b/src/stats/univariate/bootstrap.rs index dbb52f54..21c91401 100644 --- a/src/stats/univariate/bootstrap.rs +++ b/src/stats/univariate/bootstrap.rs @@ -9,7 +9,10 @@ macro_rules! test { use crate::stats::univariate::{Sample, mixed, self}; quickcheck!{ - fn mean(size: usize, start: usize, nresamples: usize) -> TestResult { + fn mean(size: u8, start: u8, nresamples: u8) -> TestResult { + let size = size as usize; + let start = start as usize; + let nresamples = nresamples as usize; if let Some(v) = crate::stats::test::vec::<$ty>(size, start) { let sample = Sample::new(&v[start..]); @@ -38,7 +41,10 @@ macro_rules! test { } quickcheck!{ - fn mean_median(size: usize, start: usize, nresamples: usize) -> TestResult { + fn mean_median(size: u8, start: u8, nresamples: u8) -> TestResult { + let size = size as usize; + let start = start as usize; + let nresamples = nresamples as usize; if let Some(v) = crate::stats::test::vec::<$ty>(size, start) { let sample = Sample::new(&v[start..]); @@ -73,10 +79,15 @@ macro_rules! test { quickcheck!{ fn mixed_two_sample( - a_size: usize, a_start: usize, - b_size: usize, b_start: usize, - nresamples: usize + a_size: u8, a_start: u8, + b_size: u8, b_start: u8, + nresamples: u8 ) -> TestResult { + let a_size = a_size as usize; + let b_size = b_size as usize; + let a_start = a_start as usize; + let b_start = b_start as usize; + let nresamples = nresamples as usize; if let (Some(a), Some(b)) = (crate::stats::test::vec::<$ty>(a_size, a_start), crate::stats::test::vec::<$ty>(b_size, b_start)) { @@ -109,10 +120,15 @@ macro_rules! test { quickcheck!{ fn two_sample( - a_size: usize, a_start: usize, - b_size: usize, b_start: usize, - nresamples: usize + a_size: u8, a_start: u8, + b_size: u8, b_start: u8, + nresamples: u8 ) -> TestResult { + let a_size = a_size as usize; + let b_size = b_size as usize; + let a_start = a_start as usize; + let b_start = b_start as usize; + let nresamples = nresamples as usize; if let (Some(a), Some(b)) = (crate::stats::test::vec::<$ty>(a_size, a_start), crate::stats::test::vec::<$ty>(b_size, b_start)) { diff --git a/src/stats/univariate/kde/kernel.rs b/src/stats/univariate/kde/kernel.rs index b4204f5f..c3d0ff51 100644 --- a/src/stats/univariate/kde/kernel.rs +++ b/src/stats/univariate/kde/kernel.rs @@ -39,13 +39,15 @@ macro_rules! test { quickcheck! { fn symmetric(x: $ty) -> bool { - relative_eq!(Gaussian.evaluate(-x), Gaussian.evaluate(x)) + x.is_nan() || relative_eq!(Gaussian.evaluate(-x), Gaussian.evaluate(x)) } } // Any [a b] integral should be in the range [0 1] quickcheck! { fn integral(a: $ty, b: $ty) -> TestResult { + let a = a.sin().abs(); // map the value to [0 1] + let b = b.sin().abs(); // map the value to [0 1] const DX: $ty = 1e-3; if a > b { diff --git a/src/stats/univariate/kde/mod.rs b/src/stats/univariate/kde/mod.rs index efc27cd9..9b0836d7 100644 --- a/src/stats/univariate/kde/mod.rs +++ b/src/stats/univariate/kde/mod.rs @@ -98,7 +98,9 @@ macro_rules! test { // The [-inf inf] integral of the estimated PDF should be one quickcheck! { - fn integral(size: usize, start: usize) -> TestResult { + fn integral(size: u8, start: u8) -> TestResult { + let size = size as usize; + let start = start as usize; const DX: $ty = 1e-3; if let Some(v) = crate::stats::test::vec::<$ty>(size, start) { diff --git a/src/stats/univariate/resamples.rs b/src/stats/univariate/resamples.rs index 831bc7ab..923669d5 100644 --- a/src/stats/univariate/resamples.rs +++ b/src/stats/univariate/resamples.rs @@ -70,7 +70,9 @@ mod test { // Check that the resample is a subset of the sample quickcheck! { - fn subset(size: usize, nresamples: usize) -> TestResult { + fn subset(size: u8, nresamples: u8) -> TestResult { + let size = size as usize; + let nresamples = nresamples as usize; if size > 1 { let v: Vec<_> = (0..size).map(|i| i as f32).collect(); let sample = Sample::new(&v);