From 39178cc6335449453b759c413f82ef2b46eba5bf Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Sat, 13 Mar 2021 16:57:57 +0000 Subject: [PATCH 1/3] I saw a c++ talk on numbers and wondered if the same trick would work here. Rather than multiplying the same number each time, if we're multiplying different numbers then the ALU can reorder them (as they have fewer data dependencies) and do more of them in parallel. If radix is called with a constant (which it is 99% of the time) I'm pretty sure that llvm evaluates the if at compile time as to which algorythm to pick. I noticed that when radix == 16 it can figure out that it can shift rather than multiply which is pretty impressive in my book that it can spot that! Speedup wise I'm seeing it go from 1.4ns to 1.2ns. Not earth shattering, but not to be sniffed at either. --- library/core/src/num/mod.rs | 103 ++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f0bd976ba83d5..b550f1d611022 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -814,6 +814,9 @@ macro_rules! doit { } doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +const MULTIPLIER: &[u32] = + &[1_000_000_000, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1]; + fn from_str_radix(src: &str, radix: u32) -> Result { use self::IntErrorKind::*; use self::ParseIntError as PIE; @@ -846,37 +849,79 @@ fn from_str_radix(src: &str, radix: u32) -> Result x, - None => return Err(PIE { kind: InvalidDigit }), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(PIE { kind: PosOverflow }), - }; - result = match result.checked_add(x) { - Some(result) => result, - None => return Err(PIE { kind: PosOverflow }), - }; + if radix == 10 { + // The cpu can reorder these adds as each mul isn't dependent + // on the previous answer. + let factors = &MULTIPLIER[MULTIPLIER.len() - src.len()..]; + let mut idx = 0; + if is_positive { + // The number is positive + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(PIE { kind: InvalidDigit }), + }; + let x = match factors[idx].checked_mul(x) { + Some(result) => result, + None => return Err(PIE { kind: PosOverflow }), + }; + result = match result.checked_add(x) { + Some(result) => result, + None => return Err(PIE { kind: PosOverflow }), + }; + idx += 1; + } + } else { + // The number is negative + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(PIE { kind: InvalidDigit }), + }; + let x = match factors[idx].checked_mul(x) { + Some(result) => result, + None => return Err(PIE { kind: NegOverflow }), + }; + result = match result.checked_sub(x) { + Some(result) => result, + None => return Err(PIE { kind: NegOverflow }), + }; + idx += 1; + } } } else { - // The number is negative - for &c in digits { - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(PIE { kind: InvalidDigit }), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(PIE { kind: NegOverflow }), - }; - result = match result.checked_sub(x) { - Some(result) => result, - None => return Err(PIE { kind: NegOverflow }), - }; + if is_positive { + // The number is positive + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(PIE { kind: InvalidDigit }), + }; + result = match result.checked_mul(radix) { + Some(result) => result, + None => return Err(PIE { kind: PosOverflow }), + }; + result = match result.checked_add(x) { + Some(result) => result, + None => return Err(PIE { kind: PosOverflow }), + }; + } + } else { + // The number is negative + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(PIE { kind: InvalidDigit }), + }; + result = match result.checked_mul(radix) { + Some(result) => result, + None => return Err(PIE { kind: NegOverflow }), + }; + result = match result.checked_sub(x) { + Some(result) => result, + None => return Err(PIE { kind: NegOverflow }), + }; + } } } Ok(result) From 565984063980230f831052e68af1fba7d7390f3f Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Mon, 15 Mar 2021 07:38:16 +0000 Subject: [PATCH 2/3] restrict to 32bit --- library/core/src/num/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index b550f1d611022..509736bad369e 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -849,7 +849,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result() <= mem::size_of::() && radix == 10 { // The cpu can reorder these adds as each mul isn't dependent // on the previous answer. let factors = &MULTIPLIER[MULTIPLIER.len() - src.len()..]; From eb371e2fd1215b3d6ab59ce09dc0787792c7ddc2 Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Sat, 20 Mar 2021 07:48:48 +0000 Subject: [PATCH 3/3] Don't include neg sign. --- library/core/src/num/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 509736bad369e..c59c281a01dab 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -850,9 +850,9 @@ fn from_str_radix(src: &str, radix: u32) -> Result() <= mem::size_of::() && radix == 10 { - // The cpu can reorder these adds as each mul isn't dependent - // on the previous answer. - let factors = &MULTIPLIER[MULTIPLIER.len() - src.len()..]; + // The ALU can reorder these adds and do more in parallel + // as each mul isn't dependent on the previous answer. + let factors = &MULTIPLIER[MULTIPLIER.len() - digits.len()..]; let mut idx = 0; if is_positive { // The number is positive