bigint: improve from_str_radix performance
Before: test from_str_radix_02 ... bench: 8,432 ns/iter (+/- 280) test from_str_radix_08 ... bench: 7,397 ns/iter (+/- 95) test from_str_radix_10 ... bench: 7,344 ns/iter (+/- 142) test from_str_radix_16 ... bench: 6,753 ns/iter (+/- 157) test from_str_radix_36 ... bench: 7,093 ns/iter (+/- 60) After: test from_str_radix_02 ... bench: 3,295 ns/iter (+/- 81) test from_str_radix_08 ... bench: 1,377 ns/iter (+/- 56) test from_str_radix_10 ... bench: 1,583 ns/iter (+/- 16) test from_str_radix_16 ... bench: 1,483 ns/iter (+/- 53) test from_str_radix_36 ... bench: 1,628 ns/iter (+/- 27)
This commit is contained in:
parent
49529895a2
commit
22ff3f918d
154
src/bigint.rs
154
src/bigint.rs
|
@ -66,12 +66,11 @@ use std::iter::repeat;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub};
|
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub};
|
||||||
use std::str::{self, FromStr};
|
use std::str::{self, FromStr};
|
||||||
use std::{cmp, fmt, hash};
|
use std::{fmt, hash};
|
||||||
use std::cmp::Ordering::{self, Less, Greater, Equal};
|
use std::cmp::Ordering::{self, Less, Greater, Equal};
|
||||||
use std::{i64, u64};
|
use std::{i64, u64};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rustc_serialize::hex::ToHex;
|
|
||||||
|
|
||||||
use traits::{ToPrimitive, FromPrimitive};
|
use traits::{ToPrimitive, FromPrimitive};
|
||||||
|
|
||||||
|
@ -254,41 +253,122 @@ impl FromStr for BigUint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read bitwise digits that evenly divide BigDigit
|
||||||
|
fn from_bitwise_digits_le(v: &[u8], bits: usize) -> BigUint {
|
||||||
|
debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits == 0);
|
||||||
|
debug_assert!(v.iter().all(|&c| (c as BigDigit) < (1 << bits)));
|
||||||
|
|
||||||
|
let digits_per_big_digit = big_digit::BITS / bits;
|
||||||
|
|
||||||
|
let data = v.chunks(digits_per_big_digit).map(|chunk| {
|
||||||
|
chunk.iter().rev().fold(0u32, |acc, &c| (acc << bits) | c as BigDigit)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
BigUint::new(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read bitwise digits that don't evenly divide BigDigit
|
||||||
|
fn from_inexact_bitwise_digits_le(v: &[u8], bits: usize) -> BigUint {
|
||||||
|
debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits != 0);
|
||||||
|
debug_assert!(v.iter().all(|&c| (c as BigDigit) < (1 << bits)));
|
||||||
|
|
||||||
|
let big_digits = (v.len() * bits + big_digit::BITS - 1) / big_digit::BITS;
|
||||||
|
let mut data = Vec::with_capacity(big_digits);
|
||||||
|
|
||||||
|
let mut d = 0;
|
||||||
|
let mut dbits = 0;
|
||||||
|
for &c in v {
|
||||||
|
d |= (c as DoubleBigDigit) << dbits;
|
||||||
|
dbits += bits;
|
||||||
|
if dbits >= big_digit::BITS {
|
||||||
|
let (hi, lo) = big_digit::from_doublebigdigit(d);
|
||||||
|
data.push(lo);
|
||||||
|
d = hi as DoubleBigDigit;
|
||||||
|
dbits -= big_digit::BITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbits > 0 {
|
||||||
|
debug_assert!(dbits < big_digit::BITS);
|
||||||
|
data.push(d as BigDigit);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigUint::new(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read little-endian radix digits
|
||||||
|
fn from_radix_digits_be(v: &[u8], radix: u32) -> BigUint {
|
||||||
|
debug_assert!(!v.is_empty() && !radix.is_power_of_two());
|
||||||
|
debug_assert!(v.iter().all(|&c| (c as u32) < radix));
|
||||||
|
|
||||||
|
let (base, power) = get_radix_base(radix);
|
||||||
|
debug_assert!(base < (1 << 32));
|
||||||
|
let base = base as BigDigit;
|
||||||
|
|
||||||
|
let r = v.len() % power;
|
||||||
|
let i = if r == 0 { power } else { r };
|
||||||
|
let (head, tail) = v.split_at(i);
|
||||||
|
|
||||||
|
let first = head.iter().fold(0, |acc, &d| acc * radix + d as BigDigit);
|
||||||
|
let mut data = vec![first];
|
||||||
|
|
||||||
|
debug_assert!(tail.len() % power == 0);
|
||||||
|
for chunk in tail.chunks(power) {
|
||||||
|
let mut carry = 0;
|
||||||
|
data.push(0);
|
||||||
|
for d in data.iter_mut() {
|
||||||
|
*d = mac_with_carry(0, *d, base, &mut carry);
|
||||||
|
}
|
||||||
|
debug_assert!(carry == 0);
|
||||||
|
|
||||||
|
let n = chunk.iter().fold(0, |acc, &d| acc * radix + d as BigDigit);
|
||||||
|
add2(&mut data, &[n]);
|
||||||
|
|
||||||
|
if let Some(&0) = data.last() {
|
||||||
|
data.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BigUint::new(data)
|
||||||
|
}
|
||||||
|
|
||||||
impl Num for BigUint {
|
impl Num for BigUint {
|
||||||
type FromStrRadixErr = ParseBigIntError;
|
type FromStrRadixErr = ParseBigIntError;
|
||||||
|
|
||||||
/// Creates and initializes a `BigUint`.
|
/// Creates and initializes a `BigUint`.
|
||||||
#[inline]
|
|
||||||
fn from_str_radix(s: &str, radix: u32) -> Result<BigUint, ParseBigIntError> {
|
fn from_str_radix(s: &str, radix: u32) -> Result<BigUint, ParseBigIntError> {
|
||||||
let (base, unit_len) = get_radix_base(radix);
|
assert!(2 <= radix && radix <= 36, "The radix must be within 2...36");
|
||||||
let base_num = match base.to_biguint() {
|
if s.is_empty() {
|
||||||
Some(base_num) => base_num,
|
// create ParseIntError
|
||||||
None => { return Err(ParseBigIntError::Other); }
|
try!(u64::from_str_radix(s, radix));
|
||||||
};
|
unreachable!();
|
||||||
|
|
||||||
let mut end = s.len();
|
|
||||||
let mut n: BigUint = Zero::zero();
|
|
||||||
let mut power: BigUint = One::one();
|
|
||||||
loop {
|
|
||||||
let start = cmp::max(end, unit_len) - unit_len;
|
|
||||||
let d = try!(usize::from_str_radix(&s[start .. end], radix));
|
|
||||||
let d: Option<BigUint> = FromPrimitive::from_usize(d);
|
|
||||||
match d {
|
|
||||||
Some(d) => {
|
|
||||||
// FIXME(#5992): assignment operator overloads
|
|
||||||
// n += d * &power;
|
|
||||||
n = n + d * &power;
|
|
||||||
}
|
|
||||||
None => { return Err(ParseBigIntError::Other); }
|
|
||||||
}
|
|
||||||
if end <= unit_len {
|
|
||||||
return Ok(n);
|
|
||||||
}
|
|
||||||
end -= unit_len;
|
|
||||||
// FIXME(#5992): assignment operator overloads
|
|
||||||
// power *= &base_num;
|
|
||||||
power = power * &base_num;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First normalize all characters to plain digit values
|
||||||
|
let mut v = Vec::with_capacity(s.len());
|
||||||
|
for (i, c) in s.chars().enumerate() {
|
||||||
|
if let Some(d) = c.to_digit(radix) {
|
||||||
|
v.push(d as u8);
|
||||||
|
} else {
|
||||||
|
// create ParseIntError
|
||||||
|
try!(u64::from_str_radix(&s[i..], radix));
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = if radix.is_power_of_two() {
|
||||||
|
// Powers of two can use bitwise masks and shifting instead of multiplication
|
||||||
|
let bits = radix.trailing_zeros() as usize;
|
||||||
|
v.reverse();
|
||||||
|
if big_digit::BITS % bits == 0 {
|
||||||
|
from_bitwise_digits_le(&v, bits)
|
||||||
|
} else {
|
||||||
|
from_inexact_bitwise_digits_le(&v, bits)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
from_radix_digits_be(&v, radix)
|
||||||
|
};
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1351,7 +1431,9 @@ impl BigUint {
|
||||||
if bytes.is_empty() {
|
if bytes.is_empty() {
|
||||||
Zero::zero()
|
Zero::zero()
|
||||||
} else {
|
} else {
|
||||||
BigUint::parse_bytes(bytes.to_hex().as_bytes(), 16).unwrap()
|
let mut v = bytes.to_vec();
|
||||||
|
v.reverse();
|
||||||
|
BigUint::from_bytes_le(&*v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1360,9 +1442,11 @@ impl BigUint {
|
||||||
/// The bytes are in little-endian byte order.
|
/// The bytes are in little-endian byte order.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_bytes_le(bytes: &[u8]) -> BigUint {
|
pub fn from_bytes_le(bytes: &[u8]) -> BigUint {
|
||||||
let mut v = bytes.to_vec();
|
if bytes.is_empty() {
|
||||||
v.reverse();
|
Zero::zero()
|
||||||
BigUint::from_bytes_be(&*v)
|
} else {
|
||||||
|
from_bitwise_digits_le(bytes, 8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the byte representation of the `BigUint` in little-endian byte order.
|
/// Returns the byte representation of the `BigUint` in little-endian byte order.
|
||||||
|
|
Loading…
Reference in New Issue