refactor/factor ~ polish spelling (comments, names, and exceptions)

This commit is contained in:
Roy Ivy III 2021-05-30 00:09:01 -05:00
parent ba7939e142
commit 1a37d502d1
8 changed files with 39 additions and 36 deletions

View file

@ -1,5 +1,7 @@
# Benchmarking `factor`
<!-- spell-checker:ignore (names) Daniel Lemire * Lemire's ; (misc) nohz -->
The benchmarks for `factor` are located under `tests/benches/factor`
and can be invoked with `cargo bench` in that directory.
@ -13,7 +15,7 @@ a newer version of `rustc`.
We currently use [`criterion`] to benchmark deterministic functions,
such as `gcd` and `table::factor`.
However, µbenchmarks are by nature unstable: not only are they specific to
However, microbenchmarks are by nature unstable: not only are they specific to
the hardware, operating system version, etc., but they are noisy and affected
by other tasks on the system (browser, compile jobs, etc.), which can cause
`criterion` to report spurious performance improvements and regressions.
@ -33,13 +35,13 @@ as possible:
[`criterion`]: https://bheisler.github.io/criterion.rs/book/index.html
[lemire]: https://lemire.me/blog/2018/01/16/microbenchmarking-calls-for-idealized-conditions/
[isolate a **physical** core]: https://pyperf.readthedocs.io/en/latest/system.html#isolate-cpus-on-linux
[frequency stays constant]: XXXTODO
[frequency stays constant]: ... <!-- ToDO -->
### Guidance for designing µbenchmarks
### Guidance for designing microbenchmarks
*Note:* this guidance is specific to `factor` and takes its application domain
into account; do not expect it to generalise to other projects. It is based
into account; do not expect it to generalize to other projects. It is based
on Daniel Lemire's [*Microbenchmarking calls for idealized conditions*][lemire],
which I recommend reading if you want to add benchmarks to `factor`.
@ -71,7 +73,7 @@ which I recommend reading if you want to add benchmarks to `factor`.
### Configurable statistical estimators
`criterion` always uses the arithmetic average as estimator; in µbenchmarks,
`criterion` always uses the arithmetic average as estimator; in microbenchmarks,
where the code under test is fully deterministic and the measurements are
subject to additive, positive noise, [the minimum is more appropriate][lemire].
@ -81,10 +83,10 @@ subject to additive, positive noise, [the minimum is more appropriate][lemire].
Measuring performance on real hardware is important, as it relates directly
to what users of `factor` experience; however, such measurements are subject
to the constraints of the real-world, and aren't perfectly reproducible.
Moreover, the mitigations for it (described above) aren't achievable in
Moreover, the mitigation for it (described above) isn't achievable in
virtualized, multi-tenant environments such as CI.
Instead, we could run the µbenchmarks in a simulated CPU with [`cachegrind`],
Instead, we could run the microbenchmarks in a simulated CPU with [`cachegrind`],
measure execution “time” in that model (in CI), and use it to detect and report
performance improvements and regressions.

View file

@ -13,8 +13,6 @@
//! 2 has no multiplicative inverse mode 2^64 because 2 | 2^64,
//! and in any case divisibility by two is trivial by checking the LSB.
// spell-checker:ignore (ToDO) invs newr newrp newtp outstr
#![cfg_attr(test, allow(dead_code))]
use std::env::{self, args};
@ -60,13 +58,13 @@ fn main() {
let mut x = primes.next().unwrap();
for next in primes {
// format the table
let outstr = format!("({}, {}, {}),", x, modular_inverse(x), std::u64::MAX / x);
if cols + outstr.len() > MAX_WIDTH {
write!(file, "\n {}", outstr).unwrap();
cols = 4 + outstr.len();
let output = format!("({}, {}, {}),", x, modular_inverse(x), std::u64::MAX / x);
if cols + output.len() > MAX_WIDTH {
write!(file, "\n {}", output).unwrap();
cols = 4 + output.len();
} else {
write!(file, " {}", outstr).unwrap();
cols += 1 + outstr.len();
write!(file, " {}", output).unwrap();
cols += 1 + output.len();
}
x = next;
@ -81,7 +79,7 @@ fn main() {
}
#[test]
fn test_generator_isprime() {
fn test_generator_is_prime() {
assert_eq!(Sieve::odd_primes.take(10_000).all(is_prime));
}
@ -106,5 +104,5 @@ const PREAMBLE: &str = r##"/*
// re-run src/factor/gen_tables.rs.
#[allow(clippy::unreadable_literal)]
pub const P_INVS_U64: &[(u64, u64, u64)] = &[
pub const PRIME_INVERSIONS_U64: &[(u64, u64, u64)] = &[
"##;

View file

@ -17,6 +17,7 @@ type Exponent = u8;
#[derive(Clone, Debug)]
struct Decomposition(SmallVec<[(u64, Exponent); NUM_FACTORS_INLINE]>);
// spell-checker:ignore (names) ErdősKac * Erdős Kac
// The number of factors to inline directly into a `Decomposition` object.
// As a consequence of the ErdősKac theorem, the average number of prime factors
// of integers < 10²⁵ ≃ 2⁸³ is 4, so we can use a slightly higher value.
@ -250,6 +251,7 @@ impl Distribution<Factors> for Standard {
let mut g = 1u64;
let mut n = u64::MAX;
// spell-checker:ignore (names) Adam Kalai * Kalai's
// Adam Kalai's algorithm for generating uniformly-distributed
// integers and their factorization.
//

View file

@ -21,6 +21,7 @@ impl Basis for Montgomery<u64> {
}
impl Basis for Montgomery<u32> {
// spell-checker:ignore (names) Steve Worley
// Small set of bases for the Miller-Rabin prime test, valid for all 32b integers;
// discovered by Steve Worley on 2013-05-27, see miller-rabin.appspot.com
#[allow(clippy::unreadable_literal)]
@ -121,8 +122,8 @@ mod tests {
}
fn odd_primes() -> impl Iterator<Item = u64> {
use crate::table::{NEXT_PRIME, P_INVS_U64};
P_INVS_U64
use crate::table::{NEXT_PRIME, PRIME_INVERSIONS_U64};
PRIME_INVERSIONS_U64
.iter()
.map(|(p, _, _)| *p)
.chain(iter::once(NEXT_PRIME))

View file

@ -93,7 +93,7 @@ mod tests {
gcd(a, gcd(b, c)) == gcd(gcd(a, b), c)
}
fn scalar_mult(a: u64, b: u64, k: u64) -> bool {
fn scalar_multiplication(a: u64, b: u64, k: u64) -> bool {
gcd(k * a, k * b) == k * gcd(a, b)
}

View file

@ -16,11 +16,11 @@ pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
debug_assert!(a % (one + one) == one, "{:?} is not odd", a);
let mut t = zero;
let mut newt = one;
let mut new_t = one;
let mut r = zero;
let mut newr = a;
let mut new_r = a;
while newr != zero {
while new_r != zero {
let quot = if r == zero {
// special case when we're just starting out
// This works because we know that
@ -28,15 +28,15 @@ pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
T::max_value()
} else {
r
} / newr;
} / new_r;
let newtp = t.wrapping_sub(&quot.wrapping_mul(&newt));
t = newt;
newt = newtp;
let new_tp = t.wrapping_sub(&quot.wrapping_mul(&new_t));
t = new_t;
new_t = new_tp;
let newrp = r.wrapping_sub(&quot.wrapping_mul(&newr));
r = newr;
newr = newrp;
let new_rp = r.wrapping_sub(&quot.wrapping_mul(&new_r));
r = new_r;
new_r = new_rp;
}
debug_assert_eq!(r, one);

View file

@ -69,7 +69,7 @@ impl<T: DoubleInt> Montgomery<T> {
let t_bits = T::zero().count_zeros() as usize;
debug_assert!(x < (self.n.as_double_width()) << t_bits);
// TODO: optimiiiiiiise
// TODO: optimize
let Montgomery { a, n } = self;
let m = T::from_double_width(x).wrapping_mul(a);
let nm = (n.as_double_width()) * (m.as_double_width());
@ -138,7 +138,7 @@ impl<T: DoubleInt> Arithmetic for Montgomery<T> {
r + self.n.wrapping_neg()
};
// Normalise to [0; n[
// Normalize to [0; n[
let r = if r < self.n { r } else { r - self.n };
// Check that r (reduced back to the usual representation) equals
@ -200,7 +200,7 @@ mod tests {
}
parametrized_check!(test_add);
fn test_mult<A: DoubleInt>() {
fn test_multiplication<A: DoubleInt>() {
for n in 0..100 {
let n = 2 * n + 1;
let m = Montgomery::<A>::new(n);
@ -213,7 +213,7 @@ mod tests {
}
}
}
parametrized_check!(test_mult);
parametrized_check!(test_multiplication);
fn test_roundtrip<A: DoubleInt>() {
for n in 0..100 {

View file

@ -13,7 +13,7 @@ use crate::Factors;
include!(concat!(env!("OUT_DIR"), "/prime_table.rs"));
pub fn factor(num: &mut u64, factors: &mut Factors) {
for &(prime, inv, ceil) in P_INVS_U64 {
for &(prime, inv, ceil) in PRIME_INVERSIONS_U64 {
if *num == 1 {
break;
}
@ -45,7 +45,7 @@ pub fn factor(num: &mut u64, factors: &mut Factors) {
pub const CHUNK_SIZE: usize = 8;
pub fn factor_chunk(n_s: &mut [u64; CHUNK_SIZE], f_s: &mut [Factors; CHUNK_SIZE]) {
for &(prime, inv, ceil) in P_INVS_U64 {
for &(prime, inv, ceil) in PRIME_INVERSIONS_U64 {
if n_s[0] == 1 && n_s[1] == 1 && n_s[2] == 1 && n_s[3] == 1 {
break;
}