Skip to content

Commit b67e65b

Browse files
authored
Merge pull request rust-lang#379 from rust-lang/tgross35/ntests
Streamline the way that test iteration count is determined (replace `NTESTS`)
2 parents 8d6f6a1 + ac3ff8c commit b67e65b

File tree

13 files changed

+462
-240
lines changed

13 files changed

+462
-240
lines changed

.github/workflows/main.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ jobs:
113113
rustup target add x86_64-unknown-linux-musl
114114
cargo generate-lockfile && ./ci/run-docker.sh ${{ matrix.target }}
115115
116+
- name: Print test logs if available
117+
if: always()
118+
run: if [ -f "target/test-log.txt" ]; then cat target/test-log.txt; fi
119+
shell: bash
120+
116121
clippy:
117122
name: Clippy
118123
runs-on: ubuntu-24.04

configure.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub struct Config {
88
pub manifest_dir: PathBuf,
99
pub out_dir: PathBuf,
1010
pub opt_level: u8,
11+
pub cargo_features: Vec<String>,
1112
pub target_arch: String,
1213
pub target_env: String,
1314
pub target_family: Option<String>,
@@ -22,11 +23,16 @@ impl Config {
2223
let target_features = env::var("CARGO_CFG_TARGET_FEATURE")
2324
.map(|feats| feats.split(',').map(ToOwned::to_owned).collect())
2425
.unwrap_or_default();
26+
let cargo_features = env::vars()
27+
.filter_map(|(name, _value)| name.strip_prefix("CARGO_FEATURE_").map(ToOwned::to_owned))
28+
.map(|s| s.to_lowercase().replace("_", "-"))
29+
.collect();
2530

2631
Self {
2732
manifest_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()),
2833
out_dir: PathBuf::from(env::var("OUT_DIR").unwrap()),
2934
opt_level: env::var("OPT_LEVEL").unwrap().parse().unwrap(),
35+
cargo_features,
3036
target_arch: env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
3137
target_env: env::var("CARGO_CFG_TARGET_ENV").unwrap(),
3238
target_family: env::var("CARGO_CFG_TARGET_FAMILY").ok(),
@@ -45,6 +51,7 @@ pub fn emit_libm_config(cfg: &Config) {
4551
emit_arch_cfg();
4652
emit_optimization_cfg(cfg);
4753
emit_cfg_shorthands(cfg);
54+
emit_cfg_env(cfg);
4855
emit_f16_f128_cfg(cfg);
4956
}
5057

@@ -53,6 +60,7 @@ pub fn emit_libm_config(cfg: &Config) {
5360
pub fn emit_test_config(cfg: &Config) {
5461
emit_optimization_cfg(cfg);
5562
emit_cfg_shorthands(cfg);
63+
emit_cfg_env(cfg);
5664
emit_f16_f128_cfg(cfg);
5765
}
5866

@@ -97,6 +105,13 @@ fn emit_cfg_shorthands(cfg: &Config) {
97105
}
98106
}
99107

108+
/// Reemit config that we make use of for test logging.
109+
fn emit_cfg_env(cfg: &Config) {
110+
println!("cargo:rustc-env=CFG_CARGO_FEATURES={:?}", cfg.cargo_features);
111+
println!("cargo:rustc-env=CFG_OPT_LEVEL={}", cfg.opt_level);
112+
println!("cargo:rustc-env=CFG_TARGET_FEATURES={:?}", cfg.target_features);
113+
}
114+
100115
/// Configure whether or not `f16` and `f128` support should be enabled.
101116
fn emit_f16_f128_cfg(cfg: &Config) {
102117
println!("cargo:rustc-check-cfg=cfg(f16_enabled)");

crates/libm-test/benches/random.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use std::hint::black_box;
22
use std::time::Duration;
33

44
use criterion::{Criterion, criterion_main};
5-
use libm_test::gen::{CachedInput, random};
6-
use libm_test::{CheckBasis, CheckCtx, GenerateInput, MathOp, TupleCall};
5+
use libm_test::gen::random;
6+
use libm_test::gen::random::RandomInput;
7+
use libm_test::{CheckBasis, CheckCtx, MathOp, TupleCall};
78

89
/// Benchmark with this many items to get a variety
910
const BENCH_ITER_ITEMS: usize = if cfg!(feature = "short-benchmarks") { 50 } else { 500 };
@@ -47,7 +48,7 @@ macro_rules! musl_rand_benches {
4748
fn bench_one<Op>(c: &mut Criterion, musl_extra: MuslExtra<Op::CFn>)
4849
where
4950
Op: MathOp,
50-
CachedInput: GenerateInput<Op::RustArgs>,
51+
Op::RustArgs: RandomInput,
5152
{
5253
let name = Op::NAME;
5354

crates/libm-test/src/gen.rs

Lines changed: 24 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,42 @@
11
//! Different generators that can create random or systematic bit patterns.
22
3-
use crate::GenerateInput;
43
pub mod domain_logspace;
54
pub mod edge_cases;
65
pub mod random;
76

8-
/// Helper type to turn any reusable input into a generator.
9-
#[derive(Clone, Debug, Default)]
10-
pub struct CachedInput {
11-
pub inputs_f32: Vec<(f32, f32, f32)>,
12-
pub inputs_f64: Vec<(f64, f64, f64)>,
13-
pub inputs_i32: Vec<(i32, i32, i32)>,
7+
/// A wrapper to turn any iterator into an `ExactSizeIterator`. Asserts the final result to ensure
8+
/// the provided size was correct.
9+
#[derive(Debug)]
10+
pub struct KnownSize<I> {
11+
total: u64,
12+
current: u64,
13+
iter: I,
1414
}
1515

16-
impl GenerateInput<(f32,)> for CachedInput {
17-
fn get_cases(&self) -> impl Iterator<Item = (f32,)> {
18-
self.inputs_f32.iter().map(|f| (f.0,))
16+
impl<I> KnownSize<I> {
17+
pub fn new(iter: I, total: u64) -> Self {
18+
Self { total, current: 0, iter }
1919
}
2020
}
2121

22-
impl GenerateInput<(f32, f32)> for CachedInput {
23-
fn get_cases(&self) -> impl Iterator<Item = (f32, f32)> {
24-
self.inputs_f32.iter().map(|f| (f.0, f.1))
25-
}
26-
}
27-
28-
impl GenerateInput<(i32, f32)> for CachedInput {
29-
fn get_cases(&self) -> impl Iterator<Item = (i32, f32)> {
30-
self.inputs_i32.iter().zip(self.inputs_f32.iter()).map(|(i, f)| (i.0, f.0))
31-
}
32-
}
22+
impl<I: Iterator> Iterator for KnownSize<I> {
23+
type Item = I::Item;
3324

34-
impl GenerateInput<(f32, i32)> for CachedInput {
35-
fn get_cases(&self) -> impl Iterator<Item = (f32, i32)> {
36-
GenerateInput::<(i32, f32)>::get_cases(self).map(|(i, f)| (f, i))
37-
}
38-
}
25+
fn next(&mut self) -> Option<Self::Item> {
26+
let next = self.iter.next();
27+
if next.is_some() {
28+
self.current += 1;
29+
return next;
30+
}
3931

40-
impl GenerateInput<(f32, f32, f32)> for CachedInput {
41-
fn get_cases(&self) -> impl Iterator<Item = (f32, f32, f32)> {
42-
self.inputs_f32.iter().copied()
32+
assert_eq!(self.current, self.total, "total items did not match expected");
33+
None
4334
}
44-
}
4535

46-
impl GenerateInput<(f64,)> for CachedInput {
47-
fn get_cases(&self) -> impl Iterator<Item = (f64,)> {
48-
self.inputs_f64.iter().map(|f| (f.0,))
36+
fn size_hint(&self) -> (usize, Option<usize>) {
37+
let remaining = usize::try_from(self.total - self.current).unwrap();
38+
(remaining, Some(remaining))
4939
}
5040
}
5141

52-
impl GenerateInput<(f64, f64)> for CachedInput {
53-
fn get_cases(&self) -> impl Iterator<Item = (f64, f64)> {
54-
self.inputs_f64.iter().map(|f| (f.0, f.1))
55-
}
56-
}
57-
58-
impl GenerateInput<(i32, f64)> for CachedInput {
59-
fn get_cases(&self) -> impl Iterator<Item = (i32, f64)> {
60-
self.inputs_i32.iter().zip(self.inputs_f64.iter()).map(|(i, f)| (i.0, f.0))
61-
}
62-
}
63-
64-
impl GenerateInput<(f64, i32)> for CachedInput {
65-
fn get_cases(&self) -> impl Iterator<Item = (f64, i32)> {
66-
GenerateInput::<(i32, f64)>::get_cases(self).map(|(i, f)| (f, i))
67-
}
68-
}
69-
70-
impl GenerateInput<(f64, f64, f64)> for CachedInput {
71-
fn get_cases(&self) -> impl Iterator<Item = (f64, f64, f64)> {
72-
self.inputs_f64.iter().copied()
73-
}
74-
}
42+
impl<I: Iterator> ExactSizeIterator for KnownSize<I> {}

crates/libm-test/src/gen/domain_logspace.rs

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,26 @@ use libm::support::{IntTy, MinInt};
66

77
use crate::domain::HasDomain;
88
use crate::op::OpITy;
9+
use crate::run_cfg::{GeneratorKind, iteration_count};
910
use crate::{CheckCtx, MathOp, logspace};
1011

11-
/// Number of tests to run.
12-
// FIXME(ntests): replace this with a more logical algorithm
13-
const NTESTS: usize = {
14-
if cfg!(optimizations_enabled) {
15-
if crate::emulated()
16-
|| !cfg!(target_pointer_width = "64")
17-
|| cfg!(all(target_arch = "x86_64", target_vendor = "apple"))
18-
{
19-
// Tests are pretty slow on non-64-bit targets, x86 MacOS, and targets that run
20-
// in QEMU.
21-
100_000
22-
} else {
23-
5_000_000
24-
}
25-
} else {
26-
// Without optimizations just run a quick check
27-
800
28-
}
29-
};
30-
3112
/// Create a range of logarithmically spaced inputs within a function's domain.
3213
///
3314
/// This allows us to get reasonably thorough coverage without wasting time on values that are
3415
/// NaN or out of range. Random tests will still cover values that are excluded here.
35-
pub fn get_test_cases<Op>(_ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
16+
pub fn get_test_cases<Op>(ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
3617
where
3718
Op: MathOp + HasDomain<Op::FTy>,
38-
IntTy<Op::FTy>: TryFrom<usize>,
19+
IntTy<Op::FTy>: TryFrom<u64>,
3920
RangeInclusive<IntTy<Op::FTy>>: Iterator,
4021
{
4122
let domain = Op::DOMAIN;
23+
let ntests = iteration_count(ctx, GeneratorKind::Domain, 0);
24+
25+
// We generate logspaced inputs within a specific range, excluding values that are out of
26+
// range in order to make iterations useful (random tests still cover the full range).
4227
let start = domain.range_start();
4328
let end = domain.range_end();
44-
let steps = OpITy::<Op>::try_from(NTESTS).unwrap_or(OpITy::<Op>::MAX);
29+
let steps = OpITy::<Op>::try_from(ntests).unwrap_or(OpITy::<Op>::MAX);
4530
logspace(start, end, steps).map(|v| (v,))
4631
}

crates/libm-test/src/gen/edge_cases.rs

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,11 @@
33
use libm::support::Float;
44

55
use crate::domain::HasDomain;
6+
use crate::run_cfg::{check_near_count, check_point_count};
67
use crate::{CheckCtx, FloatExt, MathOp};
78

8-
/// Number of values near an interesting point to check.
9-
// FIXME(ntests): replace this with a more logical algorithm
10-
const AROUND: usize = 100;
11-
12-
/// Functions have infinite asymptotes, limit how many we check.
13-
// FIXME(ntests): replace this with a more logical algorithm
14-
const MAX_CHECK_POINTS: usize = 10;
15-
169
/// Create a list of values around interesting points (infinities, zeroes, NaNs).
17-
pub fn get_test_cases<Op, F>(_ctx: &CheckCtx) -> impl Iterator<Item = (F,)>
10+
pub fn get_test_cases<Op, F>(ctx: &CheckCtx) -> impl Iterator<Item = (F,)>
1811
where
1912
Op: MathOp<FTy = F> + HasDomain<F>,
2013
F: Float,
@@ -25,23 +18,26 @@ where
2518
let domain_start = domain.range_start();
2619
let domain_end = domain.range_end();
2720

21+
let check_points = check_point_count(ctx);
22+
let near_points = check_near_count(ctx);
23+
2824
// Check near some notable constants
29-
count_up(F::ONE, values);
30-
count_up(F::ZERO, values);
31-
count_up(F::NEG_ONE, values);
32-
count_down(F::ONE, values);
33-
count_down(F::ZERO, values);
34-
count_down(F::NEG_ONE, values);
25+
count_up(F::ONE, near_points, values);
26+
count_up(F::ZERO, near_points, values);
27+
count_up(F::NEG_ONE, near_points, values);
28+
count_down(F::ONE, near_points, values);
29+
count_down(F::ZERO, near_points, values);
30+
count_down(F::NEG_ONE, near_points, values);
3531
values.push(F::NEG_ZERO);
3632

3733
// Check values near the extremes
38-
count_up(F::NEG_INFINITY, values);
39-
count_down(F::INFINITY, values);
40-
count_down(domain_end, values);
41-
count_up(domain_start, values);
42-
count_down(domain_start, values);
43-
count_up(domain_end, values);
44-
count_down(domain_end, values);
34+
count_up(F::NEG_INFINITY, near_points, values);
35+
count_down(F::INFINITY, near_points, values);
36+
count_down(domain_end, near_points, values);
37+
count_up(domain_start, near_points, values);
38+
count_down(domain_start, near_points, values);
39+
count_up(domain_end, near_points, values);
40+
count_down(domain_end, near_points, values);
4541

4642
// Check some special values that aren't included in the above ranges
4743
values.push(F::NAN);
@@ -50,9 +46,9 @@ where
5046
// Check around asymptotes
5147
if let Some(f) = domain.check_points {
5248
let iter = f();
53-
for x in iter.take(MAX_CHECK_POINTS) {
54-
count_up(x, values);
55-
count_down(x, values);
49+
for x in iter.take(check_points) {
50+
count_up(x, near_points, values);
51+
count_down(x, near_points, values);
5652
}
5753
}
5854

@@ -65,11 +61,11 @@ where
6561

6662
/// Add `AROUND` values starting at and including `x` and counting up. Uses the smallest possible
6763
/// increments (1 ULP).
68-
fn count_up<F: Float>(mut x: F, values: &mut Vec<F>) {
64+
fn count_up<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
6965
assert!(!x.is_nan());
7066

7167
let mut count = 0;
72-
while x < F::INFINITY && count < AROUND {
68+
while x < F::INFINITY && count < points {
7369
values.push(x);
7470
x = x.next_up();
7571
count += 1;
@@ -78,11 +74,11 @@ fn count_up<F: Float>(mut x: F, values: &mut Vec<F>) {
7874

7975
/// Add `AROUND` values starting at and including `x` and counting down. Uses the smallest possible
8076
/// increments (1 ULP).
81-
fn count_down<F: Float>(mut x: F, values: &mut Vec<F>) {
77+
fn count_down<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
8278
assert!(!x.is_nan());
8379

8480
let mut count = 0;
85-
while x > F::NEG_INFINITY && count < AROUND {
81+
while x > F::NEG_INFINITY && count < points {
8682
values.push(x);
8783
x = x.next_down();
8884
count += 1;

0 commit comments

Comments
 (0)