From 1bbaae2ee6b5b90bc13a5d7c47e63b2d523fef63 Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Wed, 31 Mar 2021 08:14:54 +0100 Subject: [PATCH 1/9] musings on scailing up trick2 --- benches/bench.rs | 173 ++++++++++------- src/lib.rs | 486 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 588 insertions(+), 71 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index dd8a69f..0bea5f9 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -8,81 +8,95 @@ const EXAMPLE_TIMESTAMP: &str = "1585201087123789"; const EXPECTED_TIMESTAMP: u64 = 1585201087123789; #[bench] -fn bench_str_parse(b: &mut Bencher) { +fn bench_str_parse_unchecked(b: &mut Bencher) { assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); + b.iter(|| str_parse_unchecked(black_box(EXAMPLE_TIMESTAMP))); } #[bench] -fn bench_naive_chars(b: &mut Bencher) { - assert_eq!(naive_chars(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +fn bench_str_parse_multiplier(b: &mut Bencher) { + assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_chars(black_box(EXAMPLE_TIMESTAMP))); + b.iter(|| str_parse_multiplier(black_box(EXAMPLE_TIMESTAMP))); } #[bench] -fn bench_naive_chars_iter(b: &mut Bencher) { - assert_eq!(naive_chars_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +fn bench_str_parse(b: &mut Bencher) { + assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_chars_iter(black_box(EXAMPLE_TIMESTAMP))); + b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); } -#[bench] -fn bench_naive_chars_and(b: &mut Bencher) { - assert_eq!(naive_chars_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_chars_and(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_chars(b: &mut Bencher) { +// assert_eq!(naive_chars(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_chars(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes(b: &mut Bencher) { - assert_eq!(naive_bytes(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_chars_iter(b: &mut Bencher) { +// assert_eq!(naive_chars_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_chars_iter(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes_iter(b: &mut Bencher) { - assert_eq!(naive_bytes_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes_iter(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_chars_and(b: &mut Bencher) { +// assert_eq!(naive_chars_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_chars_and(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes_and(b: &mut Bencher) { - assert_eq!(naive_bytes_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes_and(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes(b: &mut Bencher) { +// assert_eq!(naive_bytes(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes_and_c16(b: &mut Bencher) { - assert_eq!(naive_bytes_and_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes_and_c16(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes_iter(b: &mut Bencher) { +// assert_eq!(naive_bytes_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes_iter(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_unrolled(b: &mut Bencher) { - assert_eq!(unrolled(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| unrolled(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes_and(b: &mut Bencher) { +// assert_eq!(naive_bytes_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes_and(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_unrolled_unsafe(b: &mut Bencher) { - assert_eq!(unrolled_unsafe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| unrolled_unsafe(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes_and_c16(b: &mut Bencher) { +// assert_eq!(naive_bytes_and_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes_and_c16(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_unrolled_safe(b: &mut Bencher) { - assert_eq!(unrolled_safe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| unrolled_safe(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_unrolled(b: &mut Bencher) { +// assert_eq!(unrolled(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| unrolled(black_box(EXAMPLE_TIMESTAMP))); +// } + +// #[bench] +// fn bench_unrolled_unsafe(b: &mut Bencher) { +// assert_eq!(unrolled_unsafe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| unrolled_unsafe(black_box(EXAMPLE_TIMESTAMP))); +// } + +// #[bench] +// fn bench_unrolled_safe(b: &mut Bencher) { +// assert_eq!(unrolled_safe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| unrolled_safe(black_box(EXAMPLE_TIMESTAMP))); +// } #[bench] fn bench_trick(b: &mut Bencher) { @@ -91,20 +105,43 @@ fn bench_trick(b: &mut Bencher) { b.iter(|| trick(black_box(EXAMPLE_TIMESTAMP))); } + #[bench] -fn bench_trick_128(b: &mut Bencher) { - assert_eq!(trick_128(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +fn bench_trick2(b: &mut Bencher) { + assert_eq!(trick2(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick_128(black_box(EXAMPLE_TIMESTAMP))); + b.iter(|| trick2(black_box(EXAMPLE_TIMESTAMP))); +} + + +#[bench] +fn bench_trick2_small(b: &mut Bencher) { + assert_eq!(trick2("1234"), 1234u64); + b.bytes = "1234".len() as u64; + b.iter(|| trick2(black_box("1234"))); } #[bench] -fn bench_trick_simd(b: &mut Bencher) { - assert_eq!(trick_simd(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +fn bench_trick3_i64(b: &mut Bencher) { + assert_eq!(trick3(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP as i64); b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick_simd(black_box(EXAMPLE_TIMESTAMP))); + b.iter(|| trick3(black_box(EXAMPLE_TIMESTAMP))); } +// #[bench] +// fn bench_trick_128(b: &mut Bencher) { +// assert_eq!(trick_128(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| trick_128(black_box(EXAMPLE_TIMESTAMP))); +// } + +// #[bench] +// fn bench_trick_simd(b: &mut Bencher) { +// assert_eq!(trick_simd(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| trick_simd(black_box(EXAMPLE_TIMESTAMP))); +// } + // #[bench] // fn bench_trick_simd_8(b: &mut Bencher) { // assert_eq!(trick_simd_8(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); @@ -112,9 +149,9 @@ fn bench_trick_simd(b: &mut Bencher) { // b.iter(|| trick_simd_8(black_box(EXAMPLE_TIMESTAMP))); // } -#[bench] -fn bench_trick_simd_c16(b: &mut Bencher) { - assert_eq!(trick_simd_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick_simd_c16(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_trick_simd_c16(b: &mut Bencher) { +// assert_eq!(trick_simd_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| trick_simd_c16(black_box(EXAMPLE_TIMESTAMP))); +// } diff --git a/src/lib.rs b/src/lib.rs index 40616da..0fc49b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(unchecked_math)] + use core::arch::x86_64::{ _mm_cvtsi128_si64, _mm_lddqu_si128, _mm_madd_epi16, _mm_maddubs_epi16, _mm_packus_epi32, _mm_set1_epi8, _mm_set_epi16, _mm_set_epi8, _mm_sub_epi16, @@ -118,6 +120,52 @@ pub fn trick(s: &str) -> u64 { parse_8_chars(upper_digits) * 100000000 + parse_8_chars(lower_digits) } +pub fn trick2(s: &str) -> u64 { + let l = s.len(); + if l <= 8 { + let mut res = parse_8_chars(s); + if l < 8 { + res = res / MULTIPLIER[MULTIPLIER.len() - 1 - (8 - l)] as u64 + } + return res; + } + let (upper_digits, lower_digits) = s.split_at(l - 8); + parse_8_chars(upper_digits) * MULTIPLIER[MULTIPLIER.len() - 1 - (l-8)] as u64 + parse_8_chars(lower_digits) +} + +pub fn trick3(src: &str) -> i64 { + parse_signed64(src).unwrap() +} + +pub fn parse_signed64(src: &str) -> Result { + if src.is_empty() { + return Err(()); + } + let (is_positive, digits) = match src.as_bytes()[0] { + b'+' | b'-' if src[1..].is_empty() => { + return Err(()); + } + b'+' => (true, &src[1..]), + b'-' => (false, &src[1..]), + _ => (true, src), + }; + let i = trick2(digits); + if is_positive { + if i > i64::MAX as u64 { + return Err(()) + } + Ok(i as i64) + } else { + if i > i64::MAX as u64 + 1 { + return Err(()) + } + match 0_i64.checked_sub(i as i64) { + Some(res) => { Ok(res) }, + None => { Err(()) } + } + } +} + pub fn trick_128(s: &str) -> u64 { let s = s.as_ptr() as *const _; let mut chunk = 0_u128; @@ -175,11 +223,13 @@ pub fn trick_simd(s: &str) -> u64 { // } fn parse_8_chars(s: &str) -> u64 { - let s = s.as_ptr() as *const _; + let ss = s.as_ptr() as *const _; let mut chunk = 0; unsafe { - std::ptr::copy_nonoverlapping(s, &mut chunk, std::mem::size_of_val(&chunk)); + std::ptr::copy_nonoverlapping(ss, &mut chunk, std::mem::size_of_val(&chunk)); } + //chunk = chunk.shr((8 - s.len())); + // 1-byte mask trick (works on 4 pairs of single digits) let lower_digits = (chunk & 0x0f000f000f000f00) >> 8; @@ -195,7 +245,8 @@ fn parse_8_chars(s: &str) -> u64 { let lower_digits = (chunk & 0x0000ffff00000000) >> 32; let upper_digits = (chunk & 0x000000000000ffff) * 10000; let chunk = lower_digits + upper_digits; - + //println!("{:0x}", chunk); + chunk } @@ -243,3 +294,432 @@ pub fn trick_simd_c16(s: &str) -> u64 { ((chunk & 0xffffffff) * 100000000) + (chunk >> 32) } } + + + + + + + + + + +#[doc(hidden)] +trait FromStrRadixHelper: PartialOrd + Copy { + fn min_value() -> Self; + fn max_value() -> Self; + fn from_u32(u: u32) -> Self; + fn checked_mul(&self, other: u32) -> Option; + fn mul(&self, other: u32) -> Self; + fn checked_sub(&self, other: u32) -> Option; + fn checked_add(&self, other: u32) -> Option; + fn add(&self, other: u32) -> Self; + fn checked_shl(&self, other: u32) -> Option; + unsafe fn uunchecked_mul(&self, other: u32) -> Self; + unsafe fn uunchecked_sub(&self, other: u32) -> Self; + unsafe fn uunchecked_add(&self, other: u32) -> Self; +} + +macro_rules! doit { + ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { + // #[inline] + // fn safe_len() -> usize { $safe_len } + #[inline] + fn min_value() -> Self { Self::MIN } + #[inline] + fn max_value() -> Self { Self::MAX } + #[inline] + fn from_u32(u: u32) -> Self { u as Self } + #[inline(always)] + fn checked_mul(&self, other: u32) -> Option { + Self::checked_mul(*self, other as Self) + } + #[inline] + fn mul(&self, other: u32) -> Self { + *self * other as Self + } + #[inline] + fn checked_sub(&self, other: u32) -> Option { + Self::checked_sub(*self, other as Self) + } + #[inline(always)] + fn checked_add(&self, other: u32) -> Option { + Self::checked_add(*self, other as Self) + } + #[inline(always)] + fn add(&self, other: u32) -> Self { + *self + (other as Self) + } + #[inline] + fn checked_shl(&self, other: u32) -> Option { + Self::checked_shl(*self, other as u32) + } + #[inline] + unsafe fn uunchecked_mul(&self, other: u32) -> Self { + unsafe { + Self::unchecked_mul(*self, other as Self) + } + } + #[inline] + unsafe fn uunchecked_sub(&self, other: u32) -> Self { + unsafe { + Self::unchecked_sub(*self, other as Self) + } + } + #[inline] + unsafe fn uunchecked_add(&self, other: u32) -> Self { + unsafe { + Self::unchecked_add(*self, other as Self) + } + } + })*) +} +doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } + +use std::{num::*, ops::Shr}; + + + + +pub fn str_parse_unchecked(s: &str) -> u64 { + from_str_radix_unchecked::(s, 10).unwrap() +} + +fn from_str_radix_unchecked(src: &str, radix: u32) -> Result { + assert!( + radix >= 2 && radix <= 36, + "from_str_radix_int: must lie in the range `[2, 36]` - found {}", + radix + ); + + if src.is_empty() { + return Err(()); + } + + let is_signed_ty = T::from_u32(0) > T::min_value(); + + // all valid digits are ascii, so we will just iterate over the utf8 bytes + // and cast them to chars. .to_digit() will safely return None for anything + // other than a valid ascii digit for the given radix, including the first-byte + // of multi-byte sequences + let src = src.as_bytes(); + + let (is_positive, digits) = match src[0] { + b'+' | b'-' if src[1..].is_empty() => { + return Err(()); + } + b'+' => (true, &src[1..]), + b'-' if is_signed_ty => (false, &src[1..]), + _ => (true, src), + }; + + let mut result = T::from_u32(0); + if radix <= 16 && src.len() <= std::mem::size_of::() * 2 - if is_signed_ty { 1 } else { 0 } { + // The ALU can reorder these adds and do more in parallel + // as each mul isn't dependent on the previous answer. + unsafe { + if is_positive { + // The number is positive + for &c in digits { + //let x = c.wrapping_sub(b'0') as u32; + // if x > 9 { + // return Err(()); + // } + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(()), + }; + result = result.uunchecked_mul(radix); + result = result.uunchecked_add(x); + } + } else { + // The number is negative + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(()), + }; + result = result.uunchecked_mul(radix); + result = result.uunchecked_sub(x); + } + } + } + } else { + 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(()), + }; + result = match result.checked_mul(radix) { + Some(result) => result, + None => return Err(()), + }; + result = match result.checked_add(x) { + Some(result) => result, + None => return Err(()), + }; + } + } else { + // The number is negative + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(()), + }; + result = match result.checked_mul(radix) { + Some(result) => result, + None => return Err(()), + }; + result = match result.checked_sub(x) { + Some(result) => result, + None => return Err(()), + }; + } + } + } + Ok(result) +} + + + + + + + + + + + +pub fn str_parse_multiplier(s: &str) -> u64 { + from_str_radix_multiplier::(s, 10).unwrap() +} + + +fn from_str_radix_multiplier(src: &str, radix: u32) -> Result { + + assert!( + radix >= 2 && radix <= 36, + "from_str_radix_int: must lie in the range `[2, 36]` - found {}", + radix + ); + + if src.is_empty() { + return Err(()); + } + + // Compiler can't compile the following: + //const is_signed_ty : bool = T::MIN < T::MAX; + //If it could I might push for T::ZERO to be defined. + let is_signed_ty : bool = T::from_u32(0) > T::min_value(); + + // all valid digits are ascii, so we will just iterate over the utf8 bytes + // and cast them to chars. .to_digit() will safely return None for anything + // other than a valid ascii digit for the given radix, including the first-byte + // of multi-byte sequences + let src = src.as_bytes(); + + let (is_positive, digits) = match src[0] { + b'+' | b'-' if src[1..].is_empty() => { + return Err(()); + } + b'+' => (true, &src[1..]), + b'-' if is_signed_ty => (false, &src[1..]), + _ => (true, src), + }; + + let mut result = T::from_u32(0); + if radix == 10 { + // The ALU can reorder these adds and do more in parallel + // as each mul isn't dependent on the previous answer. + let mut todo = digits.len(); + let mut idx = MULTIPLIER.len() - todo.min(9); + if is_positive { + unsafe { + for &c in digits { + if idx == MULTIPLIER.len() { + todo -= 9; + idx = MULTIPLIER.len() - todo.min(9); + result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); + } + let mut x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(()), + }; + x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { + Some(result) => result, + None => return Err(()), + }; + result = match result.checked_add(x) { + Some(result) => result, + None => return Err(()), + }; + + idx += 1; + } + } + } else { + if is_signed_ty { + unsafe + { + for &c in digits { + if idx == MULTIPLIER.len() { + todo -= 9; + idx = MULTIPLIER.len() - todo.min(9); + result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); + } + let mut x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(()), + }; + x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { + Some(result) => result, + None => return Err(()), + }; + result = match result.checked_sub(x) { + Some(result) => result, + None => return Err(()), + }; + + idx += 1; + } + } + } + } + } else { + //snipped + } + Ok(result) +} + + + + + + + + + + +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, +]; +const XMULTIPLIER: &[[u32;10]] = &[[ + 1_000_000_000, + 100_000_000, + 10_000_000, + 1_000_000, + 100_000, + 10_000, + 1_000, + 100, + 10, + 1, +], +[ + 1_000_000_000, + 200_000_000, + 20_000_000, + 2_000_000, + 200_000, + 20_000, + 2_000, + 200, + 20, + 2, +], +[ + 1_000_000_000, + 300_000_000, + 30_000_000, + 3_000_000, + 300_000, + 30_000, + 3_000, + 300, + 30, + 3, +], +[ + 1_000_000_000, + 400_000_000, + 40_000_000, + 4_000_000, + 400_000, + 40_000, + 4_000, + 400, + 40, + 4, +], +[ + 1_000_000_000, + 500_000_000, + 50_000_000, + 5_000_000, + 500_000, + 50_000, + 5_000, + 500, + 50, + 5, +], +[ + 1_000_000_000, + 600_000_000, + 60_000_000, + 6_000_000, + 600_000, + 60_000, + 6_000, + 600, + 60, + 6, +], +[ + 1_000_000_000, + 700_000_000, + 70_000_000, + 7_000_000, + 700_000, + 70_000, + 7_000, + 700, + 70, + 7, +], +[ + 1_000_000_000, + 800_000_000, + 80_000_000, + 8_000_000, + 800_000, + 80_000, + 8_000, + 800, + 80, + 8, +], +[ + 1_000_000_000, + 900_000_000, + 90_000_000, + 9_000_000, + 900_000, + 90_000, + 9_000, + 900, + 90, + 9, +] +]; \ No newline at end of file From b5413b1e29955e3308a6fb983799b88eeac3dd6e Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Wed, 31 Mar 2021 08:30:41 +0100 Subject: [PATCH 2/9] adding in checks. --- src/lib.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0fc49b0..e1a449a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,16 +121,33 @@ pub fn trick(s: &str) -> u64 { } pub fn trick2(s: &str) -> u64 { + parse_u64(s).unwrap() +} + +pub fn parse_u64(s: &str) -> Result { + // TODO need a faster way to check this! + // for x in s.as_bytes() { + // if x.wrapping_sub(b'0') > 9 { + // return Err(()); + // } + // } let l = s.len(); if l <= 8 { let mut res = parse_8_chars(s); if l < 8 { res = res / MULTIPLIER[MULTIPLIER.len() - 1 - (8 - l)] as u64 } - return res; + return Ok(res); } let (upper_digits, lower_digits) = s.split_at(l - 8); - parse_8_chars(upper_digits) * MULTIPLIER[MULTIPLIER.len() - 1 - (l-8)] as u64 + parse_8_chars(lower_digits) + let res = match parse_8_chars(upper_digits).checked_mul(MULTIPLIER[MULTIPLIER.len() - 1 - (l-8)] as u64) { + Some(res) => { res }, + None => { return Err(()) } + }.checked_add(parse_8_chars(lower_digits)); + match res { + Some(res) => Ok(res), + None => { return Err(()) } + } } pub fn trick3(src: &str) -> i64 { From ee3fe219effd2995a456e25a02af8e7582c3fa2a Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Fri, 2 Apr 2021 21:11:29 +0100 Subject: [PATCH 3/9] Added checks to make sure digits between 0-9. --- benches/bench.rs | 41 ++--- src/lib.rs | 409 ++++++++++++++++++++++------------------------- 2 files changed, 215 insertions(+), 235 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 0bea5f9..7936431 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -7,26 +7,26 @@ use test::{black_box, Bencher}; const EXAMPLE_TIMESTAMP: &str = "1585201087123789"; const EXPECTED_TIMESTAMP: u64 = 1585201087123789; -#[bench] -fn bench_str_parse_unchecked(b: &mut Bencher) { - assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| str_parse_unchecked(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_str_parse_unchecked(b: &mut Bencher) { +// assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| str_parse_unchecked(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_str_parse_multiplier(b: &mut Bencher) { - assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| str_parse_multiplier(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_str_parse_multiplier(b: &mut Bencher) { +// assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| str_parse_multiplier(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_str_parse(b: &mut Bencher) { - assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_str_parse(b: &mut Bencher) { +// assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); +// } // #[bench] // fn bench_naive_chars(b: &mut Bencher) { @@ -105,7 +105,6 @@ fn bench_trick(b: &mut Bencher) { b.iter(|| trick(black_box(EXAMPLE_TIMESTAMP))); } - #[bench] fn bench_trick2(b: &mut Bencher) { assert_eq!(trick2(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); @@ -113,9 +112,11 @@ fn bench_trick2(b: &mut Bencher) { b.iter(|| trick2(black_box(EXAMPLE_TIMESTAMP))); } - #[bench] fn bench_trick2_small(b: &mut Bencher) { + assert_eq!(parse_u64(":1234"), Err(())); + assert_eq!(trick2("12345"), 12345u64); + assert_eq!(parse_u64("1234/"), Err(())); assert_eq!(trick2("1234"), 1234u64); b.bytes = "1234".len() as u64; b.iter(|| trick2(black_box("1234"))); diff --git a/src/lib.rs b/src/lib.rs index e1a449a..45c9f71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,36 +117,33 @@ pub fn unrolled_safe(s: &str) -> u64 { pub fn trick(s: &str) -> u64 { let (upper_digits, lower_digits) = s.split_at(8); - parse_8_chars(upper_digits) * 100000000 + parse_8_chars(lower_digits) + parse_8_chars(upper_digits).unwrap() * 100000000 + parse_8_chars(lower_digits).unwrap() } pub fn trick2(s: &str) -> u64 { parse_u64(s).unwrap() } -pub fn parse_u64(s: &str) -> Result { - // TODO need a faster way to check this! - // for x in s.as_bytes() { - // if x.wrapping_sub(b'0') > 9 { - // return Err(()); - // } - // } +pub fn parse_u64(s: &str) -> Result { let l = s.len(); if l <= 8 { - let mut res = parse_8_chars(s); + let mut res = parse_8_chars(s)?; if l < 8 { res = res / MULTIPLIER[MULTIPLIER.len() - 1 - (8 - l)] as u64 } return Ok(res); } let (upper_digits, lower_digits) = s.split_at(l - 8); - let res = match parse_8_chars(upper_digits).checked_mul(MULTIPLIER[MULTIPLIER.len() - 1 - (l-8)] as u64) { - Some(res) => { res }, - None => { return Err(()) } - }.checked_add(parse_8_chars(lower_digits)); + let res = match parse_8_chars(upper_digits)? + .checked_mul(MULTIPLIER[MULTIPLIER.len() - 1 - (l - 8)] as u64) + { + Some(res) => res, + None => return Err(()), + } + .checked_add(parse_8_chars(lower_digits)?); match res { Some(res) => Ok(res), - None => { return Err(()) } + None => return Err(()), } } @@ -154,7 +151,7 @@ pub fn trick3(src: &str) -> i64 { parse_signed64(src).unwrap() } -pub fn parse_signed64(src: &str) -> Result { +pub fn parse_signed64(src: &str) -> Result { if src.is_empty() { return Err(()); } @@ -169,16 +166,16 @@ pub fn parse_signed64(src: &str) -> Result { let i = trick2(digits); if is_positive { if i > i64::MAX as u64 { - return Err(()) + return Err(()); } Ok(i as i64) } else { if i > i64::MAX as u64 + 1 { - return Err(()) + return Err(()); } match 0_i64.checked_sub(i as i64) { - Some(res) => { Ok(res) }, - None => { Err(()) } + Some(res) => Ok(res), + None => Err(()), } } } @@ -239,14 +236,31 @@ pub fn trick_simd(s: &str) -> u64 { // parse_8_chars_simd(lower_digits) // } -fn parse_8_chars(s: &str) -> u64 { - let ss = s.as_ptr() as *const _; +fn parse_8_chars(s: &str) -> Result { + const MASK_HI: u64 = 0xf0f0f0f0f0f0f0f0u64; + const MASK_LOW: u64 = 0x0f0f0f0f0f0f0f0fu64; + const M3: u64 = 0x3030303030303030u64; let mut chunk = 0; unsafe { - std::ptr::copy_nonoverlapping(ss, &mut chunk, std::mem::size_of_val(&chunk)); + std::ptr::copy_nonoverlapping( + s.as_ptr() as *const _, + &mut chunk, + std::mem::size_of_val(&chunk), + ); + } + + // Make bit pattern regular if < 8 chars by prefixing with b'0's: + let chunk = chunk | 0x3030303030303030u64 << s.len() * 8; + + // See https://graphics.stanford.edu/~seander/bithacks.html#HasMoreInWord + let x = chunk & MASK_LOW; + const RESULT_MASK: u64 = !0u64 / 255 * 128; + const N: u64 = 9; + const N_MASK: u64 = !0u64 / 255 * (127 - N); + if (chunk & MASK_HI) != M3 || (x + N_MASK | x) & RESULT_MASK > 0 { + // _mm_cmpgt_epi8 would also work nicely here if available on target. + return Err(()); } - //chunk = chunk.shr((8 - s.len())); - // 1-byte mask trick (works on 4 pairs of single digits) let lower_digits = (chunk & 0x0f000f000f000f00) >> 8; @@ -262,9 +276,7 @@ fn parse_8_chars(s: &str) -> u64 { let lower_digits = (chunk & 0x0000ffff00000000) >> 32; let upper_digits = (chunk & 0x000000000000ffff) * 10000; let chunk = lower_digits + upper_digits; - //println!("{:0x}", chunk); - - chunk + Ok(chunk) } // fn parse_8_chars_simd(s: &str) -> u64 { @@ -312,15 +324,6 @@ pub fn trick_simd_c16(s: &str) -> u64 { } } - - - - - - - - - #[doc(hidden)] trait FromStrRadixHelper: PartialOrd + Copy { fn min_value() -> Self; @@ -395,9 +398,6 @@ doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } use std::{num::*, ops::Shr}; - - - pub fn str_parse_unchecked(s: &str) -> u64 { from_str_radix_unchecked::(s, 10).unwrap() } @@ -431,7 +431,7 @@ fn from_str_radix_unchecked(src: &str, radix: u32) -> Res }; let mut result = T::from_u32(0); - if radix <= 16 && src.len() <= std::mem::size_of::() * 2 - if is_signed_ty { 1 } else { 0 } { + if radix <= 16 && src.len() <= std::mem::size_of::() * 2 - if is_signed_ty { 1 } else { 0 } { // The ALU can reorder these adds and do more in parallel // as each mul isn't dependent on the previous answer. unsafe { @@ -499,23 +499,11 @@ fn from_str_radix_unchecked(src: &str, radix: u32) -> Res Ok(result) } - - - - - - - - - - pub fn str_parse_multiplier(s: &str) -> u64 { from_str_radix_multiplier::(s, 10).unwrap() } - fn from_str_radix_multiplier(src: &str, radix: u32) -> Result { - assert!( radix >= 2 && radix <= 36, "from_str_radix_int: must lie in the range `[2, 36]` - found {}", @@ -529,7 +517,7 @@ fn from_str_radix_multiplier(src: &str, radix: u32) -> Re // Compiler can't compile the following: //const is_signed_ty : bool = T::MIN < T::MAX; //If it could I might push for T::ZERO to be defined. - let is_signed_ty : bool = T::from_u32(0) > T::min_value(); + let is_signed_ty: bool = T::from_u32(0) > T::min_value(); // all valid digits are ascii, so we will just iterate over the utf8 bytes // and cast them to chars. .to_digit() will safely return None for anything @@ -554,71 +542,61 @@ fn from_str_radix_multiplier(src: &str, radix: u32) -> Re let mut idx = MULTIPLIER.len() - todo.min(9); if is_positive { unsafe { - for &c in digits { - if idx == MULTIPLIER.len() { - todo -= 9; - idx = MULTIPLIER.len() - todo.min(9); - result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); - } - let mut x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(()), - }; - x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { - Some(result) => result, - None => return Err(()), - }; - result = match result.checked_add(x) { - Some(result) => result, - None => return Err(()), - }; + for &c in digits { + if idx == MULTIPLIER.len() { + todo -= 9; + idx = MULTIPLIER.len() - todo.min(9); + result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); + } + let mut x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(()), + }; + x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { + Some(result) => result, + None => return Err(()), + }; + result = match result.checked_add(x) { + Some(result) => result, + None => return Err(()), + }; - idx += 1; - } + idx += 1; + } } } else { if is_signed_ty { - unsafe - { - for &c in digits { - if idx == MULTIPLIER.len() { - todo -= 9; - idx = MULTIPLIER.len() - todo.min(9); - result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); + unsafe { + for &c in digits { + if idx == MULTIPLIER.len() { + todo -= 9; + idx = MULTIPLIER.len() - todo.min(9); + result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); + } + let mut x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(()), + }; + x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { + Some(result) => result, + None => return Err(()), + }; + result = match result.checked_sub(x) { + Some(result) => result, + None => return Err(()), + }; + + idx += 1; + } } - let mut x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(()), - }; - x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { - Some(result) => result, - None => return Err(()), - }; - result = match result.checked_sub(x) { - Some(result) => result, - None => return Err(()), - }; - - idx += 1; - } - } } } } else { - //snipped + //snipped } Ok(result) } - - - - - - - - - const MULTIPLIER: &[u32] = &[ 1_000_000_000, 100_000_000, @@ -631,112 +609,113 @@ const MULTIPLIER: &[u32] = &[ 10, 1, ]; -const XMULTIPLIER: &[[u32;10]] = &[[ - 1_000_000_000, - 100_000_000, - 10_000_000, - 1_000_000, - 100_000, - 10_000, - 1_000, - 100, - 10, - 1, -], -[ - 1_000_000_000, - 200_000_000, - 20_000_000, - 2_000_000, - 200_000, - 20_000, - 2_000, - 200, - 20, - 2, -], -[ - 1_000_000_000, - 300_000_000, - 30_000_000, - 3_000_000, - 300_000, - 30_000, - 3_000, - 300, - 30, - 3, -], -[ - 1_000_000_000, - 400_000_000, - 40_000_000, - 4_000_000, - 400_000, - 40_000, - 4_000, - 400, - 40, - 4, -], -[ - 1_000_000_000, - 500_000_000, - 50_000_000, - 5_000_000, - 500_000, - 50_000, - 5_000, - 500, - 50, - 5, -], -[ - 1_000_000_000, - 600_000_000, - 60_000_000, - 6_000_000, - 600_000, - 60_000, - 6_000, - 600, - 60, - 6, -], -[ - 1_000_000_000, - 700_000_000, - 70_000_000, - 7_000_000, - 700_000, - 70_000, - 7_000, - 700, - 70, - 7, -], -[ - 1_000_000_000, - 800_000_000, - 80_000_000, - 8_000_000, - 800_000, - 80_000, - 8_000, - 800, - 80, - 8, -], -[ - 1_000_000_000, - 900_000_000, - 90_000_000, - 9_000_000, - 900_000, - 90_000, - 9_000, - 900, - 90, - 9, -] -]; \ No newline at end of file +const XMULTIPLIER: &[[u32; 10]] = &[ + [ + 1_000_000_000, + 100_000_000, + 10_000_000, + 1_000_000, + 100_000, + 10_000, + 1_000, + 100, + 10, + 1, + ], + [ + 1_000_000_000, + 200_000_000, + 20_000_000, + 2_000_000, + 200_000, + 20_000, + 2_000, + 200, + 20, + 2, + ], + [ + 1_000_000_000, + 300_000_000, + 30_000_000, + 3_000_000, + 300_000, + 30_000, + 3_000, + 300, + 30, + 3, + ], + [ + 1_000_000_000, + 400_000_000, + 40_000_000, + 4_000_000, + 400_000, + 40_000, + 4_000, + 400, + 40, + 4, + ], + [ + 1_000_000_000, + 500_000_000, + 50_000_000, + 5_000_000, + 500_000, + 50_000, + 5_000, + 500, + 50, + 5, + ], + [ + 1_000_000_000, + 600_000_000, + 60_000_000, + 6_000_000, + 600_000, + 60_000, + 6_000, + 600, + 60, + 6, + ], + [ + 1_000_000_000, + 700_000_000, + 70_000_000, + 7_000_000, + 700_000, + 70_000, + 7_000, + 700, + 70, + 7, + ], + [ + 1_000_000_000, + 800_000_000, + 80_000_000, + 8_000_000, + 800_000, + 80_000, + 8_000, + 800, + 80, + 8, + ], + [ + 1_000_000_000, + 900_000_000, + 90_000_000, + 9_000_000, + 900_000, + 90_000, + 9_000, + 900, + 90, + 9, + ], +]; From 21e8be8cd534fa81a39a80dff4850fba1835688f Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Fri, 2 Apr 2021 21:34:06 +0100 Subject: [PATCH 4/9] better naming --- benches/bench.rs | 20 ++++++++++---------- src/lib.rs | 43 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 7936431..6ee783a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -106,27 +106,27 @@ fn bench_trick(b: &mut Bencher) { } #[bench] -fn bench_trick2(b: &mut Bencher) { - assert_eq!(trick2(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +fn bench_trick_with_checks(b: &mut Bencher) { + assert_eq!(trick_with_checks(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick2(black_box(EXAMPLE_TIMESTAMP))); + b.iter(|| trick_with_checks(black_box(EXAMPLE_TIMESTAMP))); } #[bench] -fn bench_trick2_small(b: &mut Bencher) { +fn bench_trick_with_checks_small_u64(b: &mut Bencher) { assert_eq!(parse_u64(":1234"), Err(())); - assert_eq!(trick2("12345"), 12345u64); + assert_eq!(trick_with_checks("12345"), 12345u64); assert_eq!(parse_u64("1234/"), Err(())); - assert_eq!(trick2("1234"), 1234u64); + assert_eq!(trick_with_checks("1234"), 1234u64); b.bytes = "1234".len() as u64; - b.iter(|| trick2(black_box("1234"))); + b.iter(|| trick_with_checks(black_box("1234"))); } #[bench] -fn bench_trick3_i64(b: &mut Bencher) { - assert_eq!(trick3(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP as i64); +fn bench_trick_with_checks_i64(b: &mut Bencher) { + assert_eq!(trick_with_checks_i64(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP as i64); b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick3(black_box(EXAMPLE_TIMESTAMP))); + b.iter(|| trick_with_checks_i64(black_box(EXAMPLE_TIMESTAMP))); } // #[bench] diff --git a/src/lib.rs b/src/lib.rs index 45c9f71..ca1477b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,10 +117,10 @@ pub fn unrolled_safe(s: &str) -> u64 { pub fn trick(s: &str) -> u64 { let (upper_digits, lower_digits) = s.split_at(8); - parse_8_chars(upper_digits).unwrap() * 100000000 + parse_8_chars(lower_digits).unwrap() + parse_8_chars_unchecked(upper_digits) * 100000000 + parse_8_chars_unchecked(lower_digits) } -pub fn trick2(s: &str) -> u64 { +pub fn trick_with_checks(s: &str) -> u64 { parse_u64(s).unwrap() } @@ -135,19 +135,18 @@ pub fn parse_u64(s: &str) -> Result { } let (upper_digits, lower_digits) = s.split_at(l - 8); let res = match parse_8_chars(upper_digits)? - .checked_mul(MULTIPLIER[MULTIPLIER.len() - 1 - (l - 8)] as u64) + .checked_mul(MULTIPLIER[MULTIPLIER.len() - l + 7] as u64) { Some(res) => res, None => return Err(()), - } - .checked_add(parse_8_chars(lower_digits)?); - match res { + }; + match res.checked_add(parse_8_chars(lower_digits)?) { Some(res) => Ok(res), None => return Err(()), } } -pub fn trick3(src: &str) -> i64 { +pub fn trick_with_checks_i64(src: &str) -> i64 { parse_signed64(src).unwrap() } @@ -163,13 +162,14 @@ pub fn parse_signed64(src: &str) -> Result { b'-' => (false, &src[1..]), _ => (true, src), }; - let i = trick2(digits); + let i = parse_u64(digits)?; if is_positive { if i > i64::MAX as u64 { return Err(()); } Ok(i as i64) } else { + // Negative if i > i64::MAX as u64 + 1 { return Err(()); } @@ -279,6 +279,33 @@ fn parse_8_chars(s: &str) -> Result { Ok(chunk) } +fn parse_8_chars_unchecked(s: &str) -> u64 { + let mut chunk = 0; + unsafe { + std::ptr::copy_nonoverlapping( + s.as_ptr() as *const _, + &mut chunk, + std::mem::size_of_val(&chunk), + ); + } + + // 1-byte mask trick (works on 4 pairs of single digits) + let lower_digits = (chunk & 0x0f000f000f000f00) >> 8; + let upper_digits = (chunk & 0x000f000f000f000f) * 10; + let chunk = lower_digits + upper_digits; + + // 2-byte mask trick (works on 2 pairs of two digits) + let lower_digits = (chunk & 0x00ff000000ff0000) >> 16; + let upper_digits = (chunk & 0x000000ff000000ff) * 100; + let chunk = lower_digits + upper_digits; + + // 4-byte mask trick (works on a pair of four digits) + let lower_digits = (chunk & 0x0000ffff00000000) >> 32; + let upper_digits = (chunk & 0x000000000000ffff) * 10000; + let chunk = lower_digits + upper_digits; + chunk +} + // fn parse_8_chars_simd(s: &str) -> u64 { // unsafe { // let chunk = _mm_loadu_si64(std::mem::transmute_copy(&s)); From 85c43786d36c69cd4e720e091c5057a6148736b1 Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Fri, 2 Apr 2021 21:47:54 +0100 Subject: [PATCH 5/9] removing the other way to not confuse things. --- benches/bench.rs | 221 +++++++++++------------- src/lib.rs | 436 +++-------------------------------------------- 2 files changed, 131 insertions(+), 526 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 6ee783a..4b46f8b 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -7,96 +7,83 @@ use test::{black_box, Bencher}; const EXAMPLE_TIMESTAMP: &str = "1585201087123789"; const EXPECTED_TIMESTAMP: u64 = 1585201087123789; -// #[bench] -// fn bench_str_parse_unchecked(b: &mut Bencher) { -// assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| str_parse_unchecked(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_str_parse_multiplier(b: &mut Bencher) { -// assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| str_parse_multiplier(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_str_parse(b: &mut Bencher) { -// assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_naive_chars(b: &mut Bencher) { -// assert_eq!(naive_chars(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| naive_chars(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_naive_chars_iter(b: &mut Bencher) { -// assert_eq!(naive_chars_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| naive_chars_iter(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_naive_chars_and(b: &mut Bencher) { -// assert_eq!(naive_chars_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| naive_chars_and(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_naive_bytes(b: &mut Bencher) { -// assert_eq!(naive_bytes(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| naive_bytes(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_naive_bytes_iter(b: &mut Bencher) { -// assert_eq!(naive_bytes_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| naive_bytes_iter(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_naive_bytes_and(b: &mut Bencher) { -// assert_eq!(naive_bytes_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| naive_bytes_and(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_naive_bytes_and_c16(b: &mut Bencher) { -// assert_eq!(naive_bytes_and_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| naive_bytes_and_c16(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_unrolled(b: &mut Bencher) { -// assert_eq!(unrolled(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| unrolled(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_unrolled_unsafe(b: &mut Bencher) { -// assert_eq!(unrolled_unsafe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| unrolled_unsafe(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_unrolled_safe(b: &mut Bencher) { -// assert_eq!(unrolled_safe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| unrolled_safe(black_box(EXAMPLE_TIMESTAMP))); -// } + +#[bench] +fn bench_str_parse(b: &mut Bencher) { + assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_naive_chars(b: &mut Bencher) { + assert_eq!(naive_chars(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| naive_chars(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_naive_chars_iter(b: &mut Bencher) { + assert_eq!(naive_chars_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| naive_chars_iter(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_naive_chars_and(b: &mut Bencher) { + assert_eq!(naive_chars_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| naive_chars_and(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_naive_bytes(b: &mut Bencher) { + assert_eq!(naive_bytes(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| naive_bytes(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_naive_bytes_iter(b: &mut Bencher) { + assert_eq!(naive_bytes_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| naive_bytes_iter(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_naive_bytes_and(b: &mut Bencher) { + assert_eq!(naive_bytes_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| naive_bytes_and(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_naive_bytes_and_c16(b: &mut Bencher) { + assert_eq!(naive_bytes_and_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| naive_bytes_and_c16(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_unrolled(b: &mut Bencher) { + assert_eq!(unrolled(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| unrolled(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_unrolled_unsafe(b: &mut Bencher) { + assert_eq!(unrolled_unsafe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| unrolled_unsafe(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_unrolled_safe(b: &mut Bencher) { + assert_eq!(unrolled_safe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| unrolled_safe(black_box(EXAMPLE_TIMESTAMP))); +} #[bench] fn bench_trick(b: &mut Bencher) { @@ -129,30 +116,30 @@ fn bench_trick_with_checks_i64(b: &mut Bencher) { b.iter(|| trick_with_checks_i64(black_box(EXAMPLE_TIMESTAMP))); } -// #[bench] -// fn bench_trick_128(b: &mut Bencher) { -// assert_eq!(trick_128(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| trick_128(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_trick_simd(b: &mut Bencher) { -// assert_eq!(trick_simd(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| trick_simd(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_trick_simd_8(b: &mut Bencher) { -// assert_eq!(trick_simd_8(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| trick_simd_8(black_box(EXAMPLE_TIMESTAMP))); -// } - -// #[bench] -// fn bench_trick_simd_c16(b: &mut Bencher) { -// assert_eq!(trick_simd_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); -// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; -// b.iter(|| trick_simd_c16(black_box(EXAMPLE_TIMESTAMP))); -// } +#[bench] +fn bench_trick_128(b: &mut Bencher) { + assert_eq!(trick_128(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| trick_128(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_trick_simd(b: &mut Bencher) { + assert_eq!(trick_simd(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| trick_simd(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_trick_simd_8(b: &mut Bencher) { + assert_eq!(trick_simd_8(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| trick_simd_8(black_box(EXAMPLE_TIMESTAMP))); +} + +#[bench] +fn bench_trick_simd_c16(b: &mut Bencher) { + assert_eq!(trick_simd_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); + b.bytes = EXAMPLE_TIMESTAMP.len() as u64; + b.iter(|| trick_simd_c16(black_box(EXAMPLE_TIMESTAMP))); +} diff --git a/src/lib.rs b/src/lib.rs index ca1477b..b2bf7a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,11 @@ -#![feature(unchecked_math)] +//#![feature(unchecked_math)] use core::arch::x86_64::{ _mm_cvtsi128_si64, _mm_lddqu_si128, _mm_madd_epi16, _mm_maddubs_epi16, _mm_packus_epi32, _mm_set1_epi8, _mm_set_epi16, _mm_set_epi8, _mm_sub_epi16, }; - +use core::arch::x86_64::_mm_loadu_si64; +use core::arch::x86_64::_mm_cvtsi128_si32; pub fn str_parse(s: &str) -> u64 { s.parse().unwrap() } @@ -231,10 +232,10 @@ pub fn trick_simd(s: &str) -> u64 { } } -// pub fn trick_simd_8(s: &str) -> u64 { -// let (upper_digits, lower_digits) = s.split_at(8); -// parse_8_chars_simd(lower_digits) -// } +pub fn trick_simd_8(s: &str) -> u64 { + let (upper_digits, lower_digits) = s.split_at(8); + parse_8_chars_simd(lower_digits) +} fn parse_8_chars(s: &str) -> Result { const MASK_HI: u64 = 0xf0f0f0f0f0f0f0f0u64; @@ -306,25 +307,25 @@ fn parse_8_chars_unchecked(s: &str) -> u64 { chunk } -// fn parse_8_chars_simd(s: &str) -> u64 { -// unsafe { -// let chunk = _mm_loadu_si64(std::mem::transmute_copy(&s)); -// let zeros = _mm_set1_epi8(b'0' as i8); -// let chunk = _mm_sub_epi16(chunk, zeros); -// -// let mult = _mm_set_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1); -// let chunk = _mm_maddubs_epi16(chunk, mult); -// -// let mult = _mm_set_epi16(100, 1, 100, 1, 100, 1, 100, 1); -// let chunk = _mm_madd_epi16(chunk, mult); -// -// let chunk = _mm_packus_epi32(chunk, chunk); -// let mult = _mm_set_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1); -// let chunk = _mm_madd_epi16(chunk, mult); -// -// _mm_cvtsi128_si32(chunk) as u64 -// } -// } +fn parse_8_chars_simd(s: &str) -> u64 { + unsafe { + let chunk = _mm_loadu_si64(std::mem::transmute_copy(&s)); + let zeros = _mm_set1_epi8(b'0' as i8); + let chunk = _mm_sub_epi16(chunk, zeros); + + let mult = _mm_set_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1); + let chunk = _mm_maddubs_epi16(chunk, mult); + + let mult = _mm_set_epi16(100, 1, 100, 1, 100, 1, 100, 1); + let chunk = _mm_madd_epi16(chunk, mult); + + let chunk = _mm_packus_epi32(chunk, chunk); + let mult = _mm_set_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1); + let chunk = _mm_madd_epi16(chunk, mult); + + _mm_cvtsi128_si32(chunk) as u64 + } +} pub fn trick_simd_c16(s: &str) -> u64 { let d: &mut [u8; 16] = &mut b"0000000000000000".clone(); @@ -351,279 +352,6 @@ pub fn trick_simd_c16(s: &str) -> u64 { } } -#[doc(hidden)] -trait FromStrRadixHelper: PartialOrd + Copy { - fn min_value() -> Self; - fn max_value() -> Self; - fn from_u32(u: u32) -> Self; - fn checked_mul(&self, other: u32) -> Option; - fn mul(&self, other: u32) -> Self; - fn checked_sub(&self, other: u32) -> Option; - fn checked_add(&self, other: u32) -> Option; - fn add(&self, other: u32) -> Self; - fn checked_shl(&self, other: u32) -> Option; - unsafe fn uunchecked_mul(&self, other: u32) -> Self; - unsafe fn uunchecked_sub(&self, other: u32) -> Self; - unsafe fn uunchecked_add(&self, other: u32) -> Self; -} - -macro_rules! doit { - ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { - // #[inline] - // fn safe_len() -> usize { $safe_len } - #[inline] - fn min_value() -> Self { Self::MIN } - #[inline] - fn max_value() -> Self { Self::MAX } - #[inline] - fn from_u32(u: u32) -> Self { u as Self } - #[inline(always)] - fn checked_mul(&self, other: u32) -> Option { - Self::checked_mul(*self, other as Self) - } - #[inline] - fn mul(&self, other: u32) -> Self { - *self * other as Self - } - #[inline] - fn checked_sub(&self, other: u32) -> Option { - Self::checked_sub(*self, other as Self) - } - #[inline(always)] - fn checked_add(&self, other: u32) -> Option { - Self::checked_add(*self, other as Self) - } - #[inline(always)] - fn add(&self, other: u32) -> Self { - *self + (other as Self) - } - #[inline] - fn checked_shl(&self, other: u32) -> Option { - Self::checked_shl(*self, other as u32) - } - #[inline] - unsafe fn uunchecked_mul(&self, other: u32) -> Self { - unsafe { - Self::unchecked_mul(*self, other as Self) - } - } - #[inline] - unsafe fn uunchecked_sub(&self, other: u32) -> Self { - unsafe { - Self::unchecked_sub(*self, other as Self) - } - } - #[inline] - unsafe fn uunchecked_add(&self, other: u32) -> Self { - unsafe { - Self::unchecked_add(*self, other as Self) - } - } - })*) -} -doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } - -use std::{num::*, ops::Shr}; - -pub fn str_parse_unchecked(s: &str) -> u64 { - from_str_radix_unchecked::(s, 10).unwrap() -} - -fn from_str_radix_unchecked(src: &str, radix: u32) -> Result { - assert!( - radix >= 2 && radix <= 36, - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); - - if src.is_empty() { - return Err(()); - } - - let is_signed_ty = T::from_u32(0) > T::min_value(); - - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); - - let (is_positive, digits) = match src[0] { - b'+' | b'-' if src[1..].is_empty() => { - return Err(()); - } - b'+' => (true, &src[1..]), - b'-' if is_signed_ty => (false, &src[1..]), - _ => (true, src), - }; - - let mut result = T::from_u32(0); - if radix <= 16 && src.len() <= std::mem::size_of::() * 2 - if is_signed_ty { 1 } else { 0 } { - // The ALU can reorder these adds and do more in parallel - // as each mul isn't dependent on the previous answer. - unsafe { - if is_positive { - // The number is positive - for &c in digits { - //let x = c.wrapping_sub(b'0') as u32; - // if x > 9 { - // return Err(()); - // } - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(()), - }; - result = result.uunchecked_mul(radix); - result = result.uunchecked_add(x); - } - } else { - // The number is negative - for &c in digits { - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(()), - }; - result = result.uunchecked_mul(radix); - result = result.uunchecked_sub(x); - } - } - } - } else { - 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(()), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(()), - }; - result = match result.checked_add(x) { - Some(result) => result, - None => return Err(()), - }; - } - } else { - // The number is negative - for &c in digits { - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(()), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(()), - }; - result = match result.checked_sub(x) { - Some(result) => result, - None => return Err(()), - }; - } - } - } - Ok(result) -} - -pub fn str_parse_multiplier(s: &str) -> u64 { - from_str_radix_multiplier::(s, 10).unwrap() -} - -fn from_str_radix_multiplier(src: &str, radix: u32) -> Result { - assert!( - radix >= 2 && radix <= 36, - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); - - if src.is_empty() { - return Err(()); - } - - // Compiler can't compile the following: - //const is_signed_ty : bool = T::MIN < T::MAX; - //If it could I might push for T::ZERO to be defined. - let is_signed_ty: bool = T::from_u32(0) > T::min_value(); - - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); - - let (is_positive, digits) = match src[0] { - b'+' | b'-' if src[1..].is_empty() => { - return Err(()); - } - b'+' => (true, &src[1..]), - b'-' if is_signed_ty => (false, &src[1..]), - _ => (true, src), - }; - - let mut result = T::from_u32(0); - if radix == 10 { - // The ALU can reorder these adds and do more in parallel - // as each mul isn't dependent on the previous answer. - let mut todo = digits.len(); - let mut idx = MULTIPLIER.len() - todo.min(9); - if is_positive { - unsafe { - for &c in digits { - if idx == MULTIPLIER.len() { - todo -= 9; - idx = MULTIPLIER.len() - todo.min(9); - result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); - } - let mut x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(()), - }; - x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { - Some(result) => result, - None => return Err(()), - }; - result = match result.checked_add(x) { - Some(result) => result, - None => return Err(()), - }; - - idx += 1; - } - } - } else { - if is_signed_ty { - unsafe { - for &c in digits { - if idx == MULTIPLIER.len() { - todo -= 9; - idx = MULTIPLIER.len() - todo.min(9); - result = result.mul(*MULTIPLIER.get_unchecked(idx - 1)); - } - let mut x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(()), - }; - x = match MULTIPLIER.get_unchecked(idx).checked_mul(x) { - Some(result) => result, - None => return Err(()), - }; - result = match result.checked_sub(x) { - Some(result) => result, - None => return Err(()), - }; - - idx += 1; - } - } - } - } - } else { - //snipped - } - Ok(result) -} - const MULTIPLIER: &[u32] = &[ 1_000_000_000, 100_000_000, @@ -635,114 +363,4 @@ const MULTIPLIER: &[u32] = &[ 100, 10, 1, -]; -const XMULTIPLIER: &[[u32; 10]] = &[ - [ - 1_000_000_000, - 100_000_000, - 10_000_000, - 1_000_000, - 100_000, - 10_000, - 1_000, - 100, - 10, - 1, - ], - [ - 1_000_000_000, - 200_000_000, - 20_000_000, - 2_000_000, - 200_000, - 20_000, - 2_000, - 200, - 20, - 2, - ], - [ - 1_000_000_000, - 300_000_000, - 30_000_000, - 3_000_000, - 300_000, - 30_000, - 3_000, - 300, - 30, - 3, - ], - [ - 1_000_000_000, - 400_000_000, - 40_000_000, - 4_000_000, - 400_000, - 40_000, - 4_000, - 400, - 40, - 4, - ], - [ - 1_000_000_000, - 500_000_000, - 50_000_000, - 5_000_000, - 500_000, - 50_000, - 5_000, - 500, - 50, - 5, - ], - [ - 1_000_000_000, - 600_000_000, - 60_000_000, - 6_000_000, - 600_000, - 60_000, - 6_000, - 600, - 60, - 6, - ], - [ - 1_000_000_000, - 700_000_000, - 70_000_000, - 7_000_000, - 700_000, - 70_000, - 7_000, - 700, - 70, - 7, - ], - [ - 1_000_000_000, - 800_000_000, - 80_000_000, - 8_000_000, - 800_000, - 80_000, - 8_000, - 800, - 80, - 8, - ], - [ - 1_000_000_000, - 900_000_000, - 90_000_000, - 9_000_000, - 900_000, - 90_000, - 9_000, - 900, - 90, - 9, - ], -]; +]; \ No newline at end of file From 340a627fb1d3a106a1a0187332035b20dcfac21d Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Fri, 2 Apr 2021 21:50:23 +0100 Subject: [PATCH 6/9] Oops this one was commented out. --- benches/bench.rs | 12 ++++++------ src/lib.rs | 40 +++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 4b46f8b..81c4b99 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -130,12 +130,12 @@ fn bench_trick_simd(b: &mut Bencher) { b.iter(|| trick_simd(black_box(EXAMPLE_TIMESTAMP))); } -#[bench] -fn bench_trick_simd_8(b: &mut Bencher) { - assert_eq!(trick_simd_8(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick_simd_8(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_trick_simd_8(b: &mut Bencher) { +// assert_eq!(trick_simd_8(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| trick_simd_8(black_box(EXAMPLE_TIMESTAMP))); +// } #[bench] fn bench_trick_simd_c16(b: &mut Bencher) { diff --git a/src/lib.rs b/src/lib.rs index b2bf7a9..ad14166 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,6 @@ use core::arch::x86_64::{ _mm_cvtsi128_si64, _mm_lddqu_si128, _mm_madd_epi16, _mm_maddubs_epi16, _mm_packus_epi32, _mm_set1_epi8, _mm_set_epi16, _mm_set_epi8, _mm_sub_epi16, }; -use core::arch::x86_64::_mm_loadu_si64; -use core::arch::x86_64::_mm_cvtsi128_si32; pub fn str_parse(s: &str) -> u64 { s.parse().unwrap() } @@ -232,10 +230,10 @@ pub fn trick_simd(s: &str) -> u64 { } } -pub fn trick_simd_8(s: &str) -> u64 { - let (upper_digits, lower_digits) = s.split_at(8); - parse_8_chars_simd(lower_digits) -} +// pub fn trick_simd_8(s: &str) -> u64 { +// let (upper_digits, lower_digits) = s.split_at(8); +// parse_8_chars_simd(lower_digits) +// } fn parse_8_chars(s: &str) -> Result { const MASK_HI: u64 = 0xf0f0f0f0f0f0f0f0u64; @@ -307,25 +305,25 @@ fn parse_8_chars_unchecked(s: &str) -> u64 { chunk } -fn parse_8_chars_simd(s: &str) -> u64 { - unsafe { - let chunk = _mm_loadu_si64(std::mem::transmute_copy(&s)); - let zeros = _mm_set1_epi8(b'0' as i8); - let chunk = _mm_sub_epi16(chunk, zeros); +// fn parse_8_chars_simd(s: &str) -> u64 { +// unsafe { +// let chunk = _mm_loadu_si64(std::mem::transmute_copy(&s)); +// let zeros = _mm_set1_epi8(b'0' as i8); +// let chunk = _mm_sub_epi16(chunk, zeros); - let mult = _mm_set_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1); - let chunk = _mm_maddubs_epi16(chunk, mult); +// let mult = _mm_set_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1); +// let chunk = _mm_maddubs_epi16(chunk, mult); - let mult = _mm_set_epi16(100, 1, 100, 1, 100, 1, 100, 1); - let chunk = _mm_madd_epi16(chunk, mult); +// let mult = _mm_set_epi16(100, 1, 100, 1, 100, 1, 100, 1); +// let chunk = _mm_madd_epi16(chunk, mult); - let chunk = _mm_packus_epi32(chunk, chunk); - let mult = _mm_set_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1); - let chunk = _mm_madd_epi16(chunk, mult); +// let chunk = _mm_packus_epi32(chunk, chunk); +// let mult = _mm_set_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1); +// let chunk = _mm_madd_epi16(chunk, mult); - _mm_cvtsi128_si32(chunk) as u64 - } -} +// _mm_cvtsi128_si32(chunk) as u64 +// } +// } pub fn trick_simd_c16(s: &str) -> u64 { let d: &mut [u8; 16] = &mut b"0000000000000000".clone(); From bcaa0877cf4f54c942f1a163982a68cd59096340 Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Fri, 2 Apr 2021 23:10:16 +0100 Subject: [PATCH 7/9] Prev version was unsafe using uninitialised memory. This shifts that memory out and also slightly faster so now small u64 with checks is same speed as trick. --- src/lib.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ad14166..06a28b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,15 +126,11 @@ pub fn trick_with_checks(s: &str) -> u64 { pub fn parse_u64(s: &str) -> Result { let l = s.len(); if l <= 8 { - let mut res = parse_8_chars(s)?; - if l < 8 { - res = res / MULTIPLIER[MULTIPLIER.len() - 1 - (8 - l)] as u64 - } - return Ok(res); + return parse_8_chars(s); } let (upper_digits, lower_digits) = s.split_at(l - 8); let res = match parse_8_chars(upper_digits)? - .checked_mul(MULTIPLIER[MULTIPLIER.len() - l + 7] as u64) + .checked_mul(MULTIPLIER[MULTIPLIER.len() - 1 - (l - 8)] as u64) { Some(res) => res, None => return Err(()), @@ -246,17 +242,17 @@ fn parse_8_chars(s: &str) -> Result { &mut chunk, std::mem::size_of_val(&chunk), ); - } - // Make bit pattern regular if < 8 chars by prefixing with b'0's: - let chunk = chunk | 0x3030303030303030u64 << s.len() * 8; + // SAFETY: Unknown memory due to < 8 len replaced with with b'0's: + chunk = chunk << ((8 - s.len()) * 8) | 0x3030303030303030u64 >> s.len() * 8; + } // See https://graphics.stanford.edu/~seander/bithacks.html#HasMoreInWord let x = chunk & MASK_LOW; const RESULT_MASK: u64 = !0u64 / 255 * 128; const N: u64 = 9; const N_MASK: u64 = !0u64 / 255 * (127 - N); - if (chunk & MASK_HI) != M3 || (x + N_MASK | x) & RESULT_MASK > 0 { + if (chunk & MASK_HI) - M3 | ((x + N_MASK | x) & RESULT_MASK) != 0 { // _mm_cmpgt_epi8 would also work nicely here if available on target. return Err(()); } From 96f11bdd86aa0f3828a0af3c3807d1cb0fa0120e Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Sat, 3 Apr 2021 08:47:28 +0100 Subject: [PATCH 8/9] sign checks for '+' in u64 --- benches/bench.rs | 172 +++++++++++++++++++++++------------------------ src/lib.rs | 37 ++++++---- tags | 107 +++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+), 100 deletions(-) create mode 100644 tags diff --git a/benches/bench.rs b/benches/bench.rs index 81c4b99..bcc7de3 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -8,82 +8,82 @@ const EXAMPLE_TIMESTAMP: &str = "1585201087123789"; const EXPECTED_TIMESTAMP: u64 = 1585201087123789; -#[bench] -fn bench_str_parse(b: &mut Bencher) { - assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_str_parse(b: &mut Bencher) { +// assert_eq!(str_parse(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| str_parse(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_chars(b: &mut Bencher) { - assert_eq!(naive_chars(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_chars(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_chars(b: &mut Bencher) { +// assert_eq!(naive_chars(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_chars(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_chars_iter(b: &mut Bencher) { - assert_eq!(naive_chars_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_chars_iter(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_chars_iter(b: &mut Bencher) { +// assert_eq!(naive_chars_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_chars_iter(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_chars_and(b: &mut Bencher) { - assert_eq!(naive_chars_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_chars_and(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_chars_and(b: &mut Bencher) { +// assert_eq!(naive_chars_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_chars_and(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes(b: &mut Bencher) { - assert_eq!(naive_bytes(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes(b: &mut Bencher) { +// assert_eq!(naive_bytes(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes_iter(b: &mut Bencher) { - assert_eq!(naive_bytes_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes_iter(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes_iter(b: &mut Bencher) { +// assert_eq!(naive_bytes_iter(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes_iter(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes_and(b: &mut Bencher) { - assert_eq!(naive_bytes_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes_and(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes_and(b: &mut Bencher) { +// assert_eq!(naive_bytes_and(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes_and(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_naive_bytes_and_c16(b: &mut Bencher) { - assert_eq!(naive_bytes_and_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| naive_bytes_and_c16(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_naive_bytes_and_c16(b: &mut Bencher) { +// assert_eq!(naive_bytes_and_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| naive_bytes_and_c16(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_unrolled(b: &mut Bencher) { - assert_eq!(unrolled(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| unrolled(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_unrolled(b: &mut Bencher) { +// assert_eq!(unrolled(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| unrolled(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_unrolled_unsafe(b: &mut Bencher) { - assert_eq!(unrolled_unsafe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| unrolled_unsafe(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_unrolled_unsafe(b: &mut Bencher) { +// assert_eq!(unrolled_unsafe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| unrolled_unsafe(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_unrolled_safe(b: &mut Bencher) { - assert_eq!(unrolled_safe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| unrolled_safe(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_unrolled_safe(b: &mut Bencher) { +// assert_eq!(unrolled_safe(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| unrolled_safe(black_box(EXAMPLE_TIMESTAMP))); +// } #[bench] fn bench_trick(b: &mut Bencher) { @@ -102,10 +102,10 @@ fn bench_trick_with_checks(b: &mut Bencher) { #[bench] fn bench_trick_with_checks_small_u64(b: &mut Bencher) { assert_eq!(parse_u64(":1234"), Err(())); - assert_eq!(trick_with_checks("12345"), 12345u64); + assert_eq!(parse_u64("12345"), Ok(12345u64)); assert_eq!(parse_u64("1234/"), Err(())); assert_eq!(trick_with_checks("1234"), 1234u64); - b.bytes = "1234".len() as u64; + b.bytes = "1234".len() as u64; b.iter(|| trick_with_checks(black_box("1234"))); } @@ -116,19 +116,19 @@ fn bench_trick_with_checks_i64(b: &mut Bencher) { b.iter(|| trick_with_checks_i64(black_box(EXAMPLE_TIMESTAMP))); } -#[bench] -fn bench_trick_128(b: &mut Bencher) { - assert_eq!(trick_128(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick_128(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_trick_128(b: &mut Bencher) { +// assert_eq!(trick_128(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| trick_128(black_box(EXAMPLE_TIMESTAMP))); +// } -#[bench] -fn bench_trick_simd(b: &mut Bencher) { - assert_eq!(trick_simd(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick_simd(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_trick_simd(b: &mut Bencher) { +// assert_eq!(trick_simd(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| trick_simd(black_box(EXAMPLE_TIMESTAMP))); +// } // #[bench] // fn bench_trick_simd_8(b: &mut Bencher) { @@ -137,9 +137,9 @@ fn bench_trick_simd(b: &mut Bencher) { // b.iter(|| trick_simd_8(black_box(EXAMPLE_TIMESTAMP))); // } -#[bench] -fn bench_trick_simd_c16(b: &mut Bencher) { - assert_eq!(trick_simd_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); - b.bytes = EXAMPLE_TIMESTAMP.len() as u64; - b.iter(|| trick_simd_c16(black_box(EXAMPLE_TIMESTAMP))); -} +// #[bench] +// fn bench_trick_simd_c16(b: &mut Bencher) { +// assert_eq!(trick_simd_c16(EXAMPLE_TIMESTAMP), EXPECTED_TIMESTAMP); +// b.bytes = EXAMPLE_TIMESTAMP.len() as u64; +// b.iter(|| trick_simd_c16(black_box(EXAMPLE_TIMESTAMP))); +// } diff --git a/src/lib.rs b/src/lib.rs index 06a28b4..76fb9b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,9 +123,15 @@ pub fn trick_with_checks(s: &str) -> u64 { parse_u64(s).unwrap() } -pub fn parse_u64(s: &str) -> Result { +pub fn parse_u64(mut s: &str) -> Result { + if s.as_bytes()[0] == b'+' { + s = &s[1..]; + } let l = s.len(); if l <= 8 { + if l == 0 { + return Err(()); + } return parse_8_chars(s); } let (upper_digits, lower_digits) = s.split_at(l - 8); @@ -146,16 +152,10 @@ pub fn trick_with_checks_i64(src: &str) -> i64 { } pub fn parse_signed64(src: &str) -> Result { - if src.is_empty() { - return Err(()); - } - let (is_positive, digits) = match src.as_bytes()[0] { - b'+' | b'-' if src[1..].is_empty() => { - return Err(()); - } - b'+' => (true, &src[1..]), - b'-' => (false, &src[1..]), - _ => (true, src), + let (is_positive, digits) = match src.as_bytes().get(0) { + None => { return Err(()); } + Some(b'-') => (false, &src[1..]), + Some(_) => (true, src), }; let i = parse_u64(digits)?; if is_positive { @@ -232,9 +232,18 @@ pub fn trick_simd(s: &str) -> u64 { // } fn parse_8_chars(s: &str) -> Result { + let l = s.len(); + // if l == 1 { + // let val = s.as_bytes()[0].wrapping_sub(b'0'); + // return if val <= 9 { + // Ok(val as u64) + // } else { + // Err(()) + // } + // } const MASK_HI: u64 = 0xf0f0f0f0f0f0f0f0u64; const MASK_LOW: u64 = 0x0f0f0f0f0f0f0f0fu64; - const M3: u64 = 0x3030303030303030u64; + const ASCII_ZEROS: u64 = 0x3030303030303030u64; let mut chunk = 0; unsafe { std::ptr::copy_nonoverlapping( @@ -244,7 +253,7 @@ fn parse_8_chars(s: &str) -> Result { ); // SAFETY: Unknown memory due to < 8 len replaced with with b'0's: - chunk = chunk << ((8 - s.len()) * 8) | 0x3030303030303030u64 >> s.len() * 8; + chunk = chunk << ((8 - l) * 8) | 0x3030303030303030u64 >> l * 8; } // See https://graphics.stanford.edu/~seander/bithacks.html#HasMoreInWord @@ -252,7 +261,7 @@ fn parse_8_chars(s: &str) -> Result { const RESULT_MASK: u64 = !0u64 / 255 * 128; const N: u64 = 9; const N_MASK: u64 = !0u64 / 255 * (127 - N); - if (chunk & MASK_HI) - M3 | ((x + N_MASK | x) & RESULT_MASK) != 0 { + if (chunk & MASK_HI) - ASCII_ZEROS | ((x + N_MASK | x) & RESULT_MASK) != 0 { // _mm_cmpgt_epi8 would also work nicely here if available on target. return Err(()); } diff --git a/tags b/tags new file mode 100644 index 0000000..9cafc67 --- /dev/null +++ b/tags @@ -0,0 +1,107 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /1208e45/ +0 target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" o array:local +0 target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" a array:deps +0 target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n array:deps.0 +0 target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" o array:local +0 target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" o array:local +1 target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" s array:deps.0 +17598535894874457435 target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" o object:outputs +2 target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" b array:deps.0 +2797684049618456168 target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" o object:outputs +3 target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n array:deps.0 +931469667778813386 target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" o object:outputs +CheckDepInfo target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" o object:local.0 +CheckDepInfo target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" o object:local.0 +CheckDepInfo target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" o object:local.0 +Faster Integer Parsing (rust port) README.md /^Faster Integer Parsing (rust port)$/;" c +bench_naive_bytes benches/bench.rs /^fn bench_naive_bytes(b: &mut Bencher) {$/;" f +bench_naive_bytes_and benches/bench.rs /^fn bench_naive_bytes_and(b: &mut Bencher) {$/;" f +bench_naive_bytes_and_c16 benches/bench.rs /^fn bench_naive_bytes_and_c16(b: &mut Bencher) {$/;" f +bench_naive_bytes_iter benches/bench.rs /^fn bench_naive_bytes_iter(b: &mut Bencher) {$/;" f +bench_naive_chars benches/bench.rs /^fn bench_naive_chars(b: &mut Bencher) {$/;" f +bench_naive_chars_and benches/bench.rs /^fn bench_naive_chars_and(b: &mut Bencher) {$/;" f +bench_naive_chars_iter benches/bench.rs /^fn bench_naive_chars_iter(b: &mut Bencher) {$/;" f +bench_str_parse benches/bench.rs /^fn bench_str_parse(b: &mut Bencher) {$/;" f +bench_trick benches/bench.rs /^fn bench_trick(b: &mut Bencher) {$/;" f +bench_trick_128 benches/bench.rs /^fn bench_trick_128(b: &mut Bencher) {$/;" f +bench_trick_simd benches/bench.rs /^fn bench_trick_simd(b: &mut Bencher) {$/;" f +bench_trick_simd_c16 benches/bench.rs /^fn bench_trick_simd_c16(b: &mut Bencher) {$/;" f +bench_unrolled benches/bench.rs /^fn bench_unrolled(b: &mut Bencher) {$/;" f +bench_unrolled_safe benches/bench.rs /^fn bench_unrolled_safe(b: &mut Bencher) {$/;" f +bench_unrolled_unsafe benches/bench.rs /^fn bench_unrolled_unsafe(b: &mut Bencher) {$/;" f +code target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" n object:outputs.17598535894874457435 +code target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" n object:outputs.2797684049618456168 +code target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" n object:outputs.931469667778813386 +compile_kind target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" n +compile_kind target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n +compile_kind target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" n +config target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" n +config target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n +config target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" n +dep_info target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" s object:local.0.CheckDepInfo +dep_info target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" s object:local.0.CheckDepInfo +dep_info target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" s object:local.0.CheckDepInfo +deps target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" a +deps target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" a +deps target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" a +features target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" s +features target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" s +features target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" s +local target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" a +local target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" a +local target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" a +metadata target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" n +metadata target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n +metadata target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" n +naive_bytes src/lib.rs /^pub fn naive_bytes(s: &str) -> u64 {$/;" f +naive_bytes_and src/lib.rs /^pub fn naive_bytes_and(s: &str) -> u64 {$/;" f +naive_bytes_and_c16 src/lib.rs /^pub fn naive_bytes_and_c16(s: &str) -> u64 {$/;" f +naive_bytes_iter src/lib.rs /^pub fn naive_bytes_iter(s: &str) -> u64 {$/;" f +naive_chars src/lib.rs /^pub fn naive_chars(s: &str) -> u64 {$/;" f +naive_chars_and src/lib.rs /^pub fn naive_chars_and(s: &str) -> u64 {$/;" f +naive_chars_iter src/lib.rs /^pub fn naive_chars_iter(s: &str) -> u64 {$/;" f +outputs target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" o +parse_8_chars src/lib.rs /^fn parse_8_chars(s: &str) -> u64 {$/;" f +path target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" n +path target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n +path target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" n +profile target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" n +profile target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n +profile target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" n +rustc target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" n +rustc target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n +rustc target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" n +rustc_fingerprint target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" n +rustflags target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" a +rustflags target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" a +rustflags target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" a +status target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.17598535894874457435 +status target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.2797684049618456168 +status target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.931469667778813386 +stderr target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.17598535894874457435 +stderr target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.2797684049618456168 +stderr target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.931469667778813386 +stdout target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.17598535894874457435 +stdout target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.2797684049618456168 +stdout target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" s object:outputs.931469667778813386 +str_parse src/lib.rs /^pub fn str_parse(s: &str) -> u64 {$/;" f +success target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" b object:outputs.17598535894874457435 +success target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" b object:outputs.2797684049618456168 +success target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" b object:outputs.931469667778813386 +successes target/.rustc_info.json /^{"rustc_fingerprint":9851385122578847373,"outputs":{"17598535894874457435":{"success":true,"stat/;" o +target target/release/.fingerprint/parseint-4bd3d8f62590d6bc/test-lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":174046132101/;" n +target target/release/.fingerprint/parseint-4c486e2887f16749/test-bench-bench.json /^{"rustc":4712542145745680002,"features":"[]","target":6628690783967570892,"profile":174046132101/;" n +target target/release/.fingerprint/parseint-e1ebe09b638d8141/lib-parseint.json /^{"rustc":4712542145745680002,"features":"[]","target":7876227839133864575,"profile":228515222820/;" n +trick src/lib.rs /^pub fn trick(s: &str) -> u64 {$/;" f +trick_128 src/lib.rs /^pub fn trick_128(s: &str) -> u64 {$/;" f +trick_simd src/lib.rs /^pub fn trick_simd(s: &str) -> u64 {$/;" f +trick_simd_c16 src/lib.rs /^pub fn trick_simd_c16(s: &str) -> u64 {$/;" f +unrolled src/lib.rs /^pub fn unrolled(s: &str) -> u64 {$/;" f +unrolled_safe src/lib.rs /^pub fn unrolled_safe(s: &str) -> u64 {$/;" f +unrolled_unsafe src/lib.rs /^pub fn unrolled_unsafe(s: &str) -> u64 {$/;" f From 450540961ab3f95bd2cf2be2a513005f344fcd35 Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Sat, 3 Apr 2021 09:00:41 +0100 Subject: [PATCH 9/9] bugfix / simplification. --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 76fb9b2..65c9788 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,8 +135,7 @@ pub fn parse_u64(mut s: &str) -> Result { return parse_8_chars(s); } let (upper_digits, lower_digits) = s.split_at(l - 8); - let res = match parse_8_chars(upper_digits)? - .checked_mul(MULTIPLIER[MULTIPLIER.len() - 1 - (l - 8)] as u64) + let res = match parse_8_chars(upper_digits)?.checked_mul(100_000_000) { Some(res) => res, None => return Err(()),