Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit 6da3286

Browse files
committed
extensive wip
check passes Running in parallel successfully progress bar Update progress bar Speed up extensive tests by chunking Clean up pb Rename the env var to enable extensive tests
1 parent f25c8e9 commit 6da3286

File tree

15 files changed

+635
-51
lines changed

15 files changed

+635
-51
lines changed

ci/run-docker.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ run() {
2929
--rm \
3030
--user "$(id -u):$(id -g)" \
3131
-e RUSTFLAGS \
32+
-e CI \
3233
-e CARGO_HOME=/cargo \
3334
-e CARGO_TARGET_DIR=/target \
3435
-e "EMULATED=$emulated" \

crates/libm-macros/src/enums.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,43 +31,74 @@ pub fn function_enum(
3131

3232
let enum_name = &item.ident;
3333
let mut as_str_arms = Vec::new();
34+
let mut from_str_arms = Vec::new();
3435
let mut base_arms = Vec::new();
36+
let mut math_op_arms = Vec::new();
3537

3638
for func in ALL_FUNCTIONS_FLAT.iter() {
3739
let fn_name = func.name;
40+
let name_ident = Ident::new(fn_name, Span::call_site());
3841
let ident = Ident::new(&fn_name.to_upper_camel_case(), Span::call_site());
3942
let bname_ident = Ident::new(&base_name(fn_name).to_upper_camel_case(), Span::call_site());
43+
let fty_ident =
44+
Ident::new(&func.base_fty.to_string().to_upper_camel_case(), Span::call_site());
4045

4146
// Match arm for `fn as_str(self)` matcher
4247
as_str_arms.push(quote! { Self::#ident => #fn_name });
48+
from_str_arms.push(quote! { #fn_name => Self::#ident });
4349

4450
// Match arm for `fn base_name(self)` matcher
4551
base_arms.push(quote! { Self::#ident => #base_enum::#bname_ident });
52+
math_op_arms.push(quote! {
53+
Self::#ident =>
54+
crate::op::MathOpInfo::new::<crate::op::#name_ident::Routine>(
55+
crate::FloatTy::#fty_ident
56+
)
57+
});
4658

4759
let variant =
4860
Variant { attrs: Vec::new(), ident, fields: Fields::Unit, discriminant: None };
4961

5062
item.variants.push(variant);
5163
}
5264

65+
let variants = item.variants.iter();
66+
5367
let res = quote! {
5468
// Instantiate the enum
5569
#item
5670

5771
impl #enum_name {
72+
pub const ALL: &[Self] = &[
73+
#( Self::#variants, )*
74+
];
75+
5876
/// The stringified version of this function name.
5977
pub const fn as_str(self) -> &'static str {
6078
match self {
6179
#( #as_str_arms , )*
6280
}
6381
}
6482

83+
pub fn from_str(s: &str) -> Self {
84+
match s {
85+
#( #from_str_arms , )*
86+
_ => panic!("Unrecognized operation `{s}`"),
87+
}
88+
}
89+
6590
/// The base name enum for this function.
6691
pub const fn base_name(self) -> #base_enum {
6792
match self {
6893
#( #base_arms, )*
6994
}
7095
}
96+
97+
pub fn math_op(self) -> crate::op::MathOpInfo {
98+
match self {
99+
#( #math_op_arms , )*
100+
}
101+
}
71102
}
72103
};
73104

crates/libm-macros/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod enums;
22
mod parse;
33

4+
use std::fmt;
45
use std::sync::LazyLock;
56

67
use parse::{Invocation, StructuredInput};
@@ -206,6 +207,26 @@ enum Ty {
206207
MutCInt,
207208
}
208209

210+
impl fmt::Display for Ty {
211+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212+
let s = match self {
213+
Ty::F16 => "f16",
214+
Ty::F32 => "f32",
215+
Ty::F64 => "f64",
216+
Ty::F128 => "f128",
217+
Ty::I32 => "i32",
218+
Ty::CInt => "core::ffi::c_int",
219+
Ty::MutF16 => "&mut f16",
220+
Ty::MutF32 => "&mut f32",
221+
Ty::MutF64 => "&mut f64",
222+
Ty::MutF128 => "&mut f128",
223+
Ty::MutI32 => "&mut i32",
224+
Ty::MutCInt => "&mut core::ffi::c_int",
225+
};
226+
f.write_str(s)
227+
}
228+
}
229+
209230
impl ToTokens for Ty {
210231
fn to_tokens(&self, tokens: &mut pm2::TokenStream) {
211232
let ts = match self {

crates/libm-macros/tests/enum.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,35 @@ fn basename() {
1717
assert_eq!(Function::Sin.base_name(), BaseName::Sin);
1818
assert_eq!(Function::Sinf.base_name(), BaseName::Sin);
1919
}
20+
21+
mod op {
22+
macro_rules! simple_mod {
23+
(
24+
fn_name: $fn_name:ident,
25+
) => {
26+
pub mod $fn_name {
27+
pub struct Routine {}
28+
}
29+
};
30+
}
31+
32+
// Test with no extra, no skip, and no attributes
33+
libm_macros::for_each_function! {
34+
callback: simple_mod,
35+
}
36+
37+
pub struct MathOpInfo {}
38+
impl MathOpInfo {
39+
#[allow(clippy::extra_unused_type_parameters)]
40+
pub fn new<T>(_fty: crate::FloatTy) -> Self {
41+
unimplemented!()
42+
}
43+
}
44+
}
45+
46+
pub enum FloatTy {
47+
F16,
48+
F32,
49+
F64,
50+
F128,
51+
}

crates/libm-test/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ short-benchmarks = []
2424
[dependencies]
2525
anyhow = "1.0.90"
2626
az = { version = "1.2.1", optional = true }
27+
indicatif = { version = "0.17.9", default-features = false }
2728
libm = { path = "../..", features = ["unstable-test-support"] }
2829
libm-macros = { path = "../libm-macros" }
2930
musl-math-sys = { path = "../musl-math-sys", optional = true }
3031
paste = "1.0.15"
3132
rand = "0.8.5"
3233
rand_chacha = "0.3.1"
34+
rayon = "1.10.0"
3335
rug = { version = "1.26.1", optional = true, default-features = false, features = ["float", "std"] }
3436

3537
[target.'cfg(target_family = "wasm")'.dependencies]
@@ -41,7 +43,15 @@ rand = { version = "0.8.5", optional = true }
4143

4244
[dev-dependencies]
4345
criterion = { version = "0.5.1", default-features = false, features = ["cargo_bench_support"] }
46+
# FIXME: use the crates.io version once it supports runtime skipping of tests
47+
libtest-mimic = { git = "https://github.com/tgross35/libtest-mimic.git", rev = "4c8413b493e1b499bb941d2ced1f4c3d8462f53c" }
4448

4549
[[bench]]
4650
name = "random"
4751
harness = false
52+
53+
[[test]]
54+
# No harness so that we can skip tests at runtime based on env. Prefixed with
55+
# `z` so these tests get run last.
56+
name = "z_extensive"
57+
harness = false

crates/libm-test/examples/plot_domains.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::path::Path;
99
use std::process::Command;
1010
use std::{env, fs};
1111

12+
use libm_test::Identifier;
1213
use libm_test::domain::Domain;
1314
use libm_test::gen::domain_logspace;
1415

@@ -24,35 +25,35 @@ fn main() {
2425
let mut j_args = Vec::new();
2526

2627
// Plot a few domains with some functions that use them.
27-
plot_one(out_dir, "sqrt", Domain::SQRT, &mut j_args);
28-
plot_one(out_dir, "cos", Domain::TRIG, &mut j_args);
29-
plot_one(out_dir, "cbrt", Domain::UNBOUNDED, &mut j_args);
28+
plot_one(out_dir, Identifier::Sqrt, Domain::SQRT, &mut j_args);
29+
plot_one(out_dir, Identifier::Cos, Domain::TRIG, &mut j_args);
30+
plot_one(out_dir, Identifier::Cbrt, Domain::UNBOUNDED, &mut j_args);
3031

3132
println!("launching script");
3233
let mut cmd = Command::new("julia");
33-
if !cfg!(debug_assertions) {
34+
if cfg!(optimizations_enabled) {
3435
cmd.arg("-O3");
3536
}
3637
cmd.arg(jl_script).args(j_args).status().unwrap();
3738
}
3839

3940
/// Plot a single domain.
40-
fn plot_one(out_dir: &Path, name: &str, domain: Domain<f32>, j_args: &mut Vec<String>) {
41-
let base_name = out_dir.join(format!("domain-inputs-{name}"));
41+
fn plot_one(out_dir: &Path, id: Identifier, domain: Domain<f32>, j_args: &mut Vec<String>) {
42+
let base_name = out_dir.join(format!("domain-inputs-{id}"));
4243
let text_file = base_name.with_extension("txt");
4344

4445
{
4546
// Scope for file and writer
4647
let f = fs::File::create(&text_file).unwrap();
4748
let mut w = BufWriter::new(f);
4849

49-
for input in domain_logspace::get_test_cases_inner::<f32>(domain) {
50+
for input in domain_logspace::get_test_cases_inner::<f32>(domain, id) {
5051
writeln!(w, "{:e}", input.0).unwrap();
5152
}
5253
w.flush().unwrap();
5354
}
5455

5556
// The julia script expects `name1 path1 name2 path2...` args
56-
j_args.push(name.to_owned());
57+
j_args.push(id.as_str().to_owned());
5758
j_args.push(base_name.to_str().unwrap().to_owned());
5859
}

crates/libm-test/src/gen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::GenerateInput;
44
pub mod domain_logspace;
55
pub mod edge_cases;
6+
pub mod extensive;
67
pub mod random;
78

89
/// Helper type to turn any reusable input into a generator.
Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,10 @@
11
//! A generator that makes use of domain bounds.
22
3-
use std::ops::Bound;
4-
53
use libm::support::{Float, MinInt};
64

75
use crate::domain::{Domain, HasDomain};
8-
use crate::{MathOp, logspace};
9-
10-
/// Number of tests to run.
11-
// FIXME(ntests): replace this with a more logical algorithm
12-
const NTESTS: usize = {
13-
if cfg!(optimizations_enabled) {
14-
if crate::emulated()
15-
|| !cfg!(target_pointer_width = "64")
16-
|| cfg!(all(target_arch = "x86_64", target_vendor = "apple"))
17-
{
18-
// Tests are pretty slow on non-64-bit targets, x86 MacOS, and targets that run
19-
// in QEMU.
20-
100_000
21-
} else {
22-
5_000_000
23-
}
24-
} else {
25-
// Without optimizations just run a quick check
26-
800
27-
}
28-
};
6+
use crate::run_cfg::{TestAction, TestTy};
7+
use crate::{MathOp, logspace, op};
298

309
/// Create a range of logarithmically spaced inputs within a function's domain.
3110
///
@@ -34,26 +13,26 @@ const NTESTS: usize = {
3413
pub fn get_test_cases<Op>() -> impl Iterator<Item = (Op::FTy,)>
3514
where
3615
Op: MathOp + HasDomain<Op::FTy>,
37-
<Op::FTy as Float>::Int: TryFrom<usize>,
16+
<Op::FTy as Float>::Int: TryFrom<u64>,
3817
{
39-
get_test_cases_inner::<Op::FTy>(Op::D)
18+
get_test_cases_inner::<Op::FTy>(Op::D, Op::IDENTIFIER)
4019
}
4120

42-
pub fn get_test_cases_inner<F>(domain: Domain<F>) -> impl Iterator<Item = (F,)>
21+
pub fn get_test_cases_inner<F>(domain: Domain<F>, id: op::Identifier) -> impl Iterator<Item = (F,)>
4322
where
44-
F: Float<Int: TryFrom<usize>>,
23+
F: Float<Int: TryFrom<u64>>,
4524
{
46-
// We generate logspaced inputs within a specific range, excluding values that are out of
47-
// range in order to make iterations useful (random tests still cover the full range).
48-
let range_start = match domain.start {
49-
Bound::Included(v) | Bound::Excluded(v) => v,
50-
Bound::Unbounded => F::NEG_INFINITY,
51-
};
52-
let range_end = match domain.end {
53-
Bound::Included(v) | Bound::Excluded(v) => v,
54-
Bound::Unbounded => F::INFINITY,
25+
let action = crate::run_cfg::get_iterations(id, TestTy::Logspace, 0);
26+
let ntests = match action {
27+
TestAction::Iterations(n) => n,
28+
TestAction::Run => unimplemented!(),
29+
TestAction::Skip => unimplemented!(),
5530
};
5631

57-
let steps = F::Int::try_from(NTESTS).unwrap_or(F::Int::MAX);
58-
logspace(range_start, range_end, steps).map(|v| (v,))
32+
// We generate logspaced inputs within a specific range, excluding values that are out of
33+
// range in order to make iterations useful (random tests still cover the full range).
34+
let start = domain.range_start();
35+
let end = domain.range_end();
36+
let steps = F::Int::try_from(ntests).unwrap_or(F::Int::MAX);
37+
logspace(start, end, steps).map(|v| (v,))
5938
}

0 commit comments

Comments
 (0)