Skip to content

Commit

Permalink
Fancy new algorithm on stable SIMD
Browse files Browse the repository at this point in the history
  • Loading branch information
Veedrac committed Oct 23, 2018
1 parent ea1b4db commit c6668a3
Show file tree
Hide file tree
Showing 10 changed files with 718 additions and 354 deletions.
13 changes: 10 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ matrix:
- rust: stable
env:
- ARCH=i686
- rust: stable
env:
- ARCH=i686
- FEATURES="--features runtime-dispatch-simd"
- rust: stable
env:
- ARCH=x86_64
- FEATURES="--features runtime-dispatch-simd"
- rust: beta
env:
- ARCH=i686
Expand All @@ -26,12 +34,11 @@ matrix:
- rust: nightly
env:
- ARCH=x86_64
- FEATURES="--features simd-accel"
- FEATURES="--features generic-simd"
- rust: nightly
env:
- ARCH=x86_64
- FEATURES="--features avx-accel"
- RUSTFLAGS="-C target-feature=+avx"
- FEATURES="--features generic-simd,runtime-dispatch-simd"
addons:
apt:
packages:
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ appveyor = { repository = "llogiq/bytecount" }
bench = false

[features]
avx-accel = ["simd-accel"]
simd-accel = ["simd"]
generic-simd = ["packed_simd"]
runtime-dispatch-simd = []
html_report = []

[dependencies]
simd = { version = "0.2.0", optional = true }
packed_simd = { version = "0.3.0", optional = true }

[dev-dependencies]
quickcheck = "0.6"
Expand Down
20 changes: 8 additions & 12 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ environment:
CHANNEL: nightly
- TARGET: i686-pc-windows-gnu
CHANNEL: nightly
FEATURES: "--features simd-accel"
FEATURES: "--features generic-simd"
- TARGET: i686-pc-windows-gnu
CHANNEL: nightly
FEATURES: "--features avx-accel"
RUSTFLAGS: "-C target-feature=+avx"
FEATURES: "--features generic-simd,runtime-dispatch-simd"
- TARGET: i686-pc-windows-msvc
CHANNEL: stable
- TARGET: i686-pc-windows-msvc
Expand All @@ -26,11 +25,10 @@ environment:
CHANNEL: nightly
- TARGET: i686-pc-windows-msvc
CHANNEL: nightly
FEATURES: "--features simd-accel"
FEATURES: "--features generic-simd"
- TARGET: i686-pc-windows-msvc
CHANNEL: nightly
FEATURES: "--features avx-accel"
RUSTFLAGS: "-C target-feature=+avx"
FEATURES: "--features generic-simd,runtime-dispatch-simd"
- TARGET: x86_64-pc-windows-gnu
CHANNEL: stable
- TARGET: x86_64-pc-windows-gnu
Expand All @@ -39,11 +37,10 @@ environment:
CHANNEL: nightly
- TARGET: x86_64-pc-windows-gnu
CHANNEL: nightly
FEATURES: "--features simd-accel"
FEATURES: "--features generic-simd"
- TARGET: x86_64-pc-windows-gnu
CHANNEL: nightly
FEATURES: "--features avx-accel"
RUSTFLAGS: "-C target-feature=+avx"
FEATURES: "--features generic-simd,runtime-dispatch-simd"
- TARGET: x86_64-pc-windows-msvc
CHANNEL: stable
- TARGET: x86_64-pc-windows-msvc
Expand All @@ -52,11 +49,10 @@ environment:
CHANNEL: nightly
- TARGET: x86_64-pc-windows-msvc
CHANNEL: nightly
FEATURES: "--features simd-accel"
FEATURES: "--features generic-simd"
- TARGET: x86_64-pc-windows-msvc
CHANNEL: nightly
FEATURES: "--features avx-accel"
RUSTFLAGS: "-C target-feature=+avx"
FEATURES: "--features generic-simd,runtime-dispatch-simd"

install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
Expand Down
111 changes: 111 additions & 0 deletions src/integer_simd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#[cfg(not(feature = "runtime-dispatch-simd"))]
use core::{mem, ptr, usize};
#[cfg(feature = "runtime-dispatch-simd")]
use std::{mem, ptr, usize};

fn splat(byte: u8) -> usize {
let lo = usize::MAX / 0xFF;
lo * byte as usize
}

unsafe fn usize_load_unchecked(bytes: &[u8], offset: usize) -> usize {
let mut output = 0;
ptr::copy_nonoverlapping(
bytes.as_ptr().offset(offset as isize),
&mut output as *mut usize as *mut u8,
mem::size_of::<usize>()
);
output
}

fn bytewise_equal(lhs: usize, rhs: usize) -> usize {
let lo = usize::MAX / 0xFF;
let hi = lo << 7;

let x = lhs ^ rhs;
!((((x & !hi) + !hi) | x) >> 7) & lo
}

fn sum_usize(values: usize) -> usize {
let every_other_byte_lo = usize::MAX / 0xFFFF;
let every_other_byte = every_other_byte_lo * 0xFF;

// Pairwise reduction to avoid overflow on next step.
let pair_sum: usize = (values & every_other_byte) + ((values >> 8) & every_other_byte);

// Multiplication results in top two bytes holding sum.
pair_sum.wrapping_mul(every_other_byte_lo) >> ((mem::size_of::<usize>() - 2) * 8)
}

fn is_leading_utf8_byte(values: usize) -> usize {
// a leading UTF-8 byte is one which does not start with the bits 10.
((!values >> 7) | (values >> 6)) & splat(1)
}

pub fn chunk_count(haystack: &[u8], needle: u8) -> usize {
let chunksize = mem::size_of::<usize>();
assert!(haystack.len() >= chunksize);

unsafe {
let mut offset = 0;
let mut count = 0;

let needles = splat(needle);

// 2040
while haystack.len() >= offset + chunksize * 255 {
let mut counts = 0;
for _ in 0..255 {
counts += bytewise_equal(usize_load_unchecked(haystack, offset), needles);
offset += chunksize;
}
count += sum_usize(counts);
}

// 8
let mut counts = 0;
for i in 0..(haystack.len() - offset) / chunksize {
counts += bytewise_equal(usize_load_unchecked(haystack, offset + i * chunksize), needles);
}
if haystack.len() % 8 != 0 {
let mask = !(!0 >> ((haystack.len() % chunksize) * 8));
counts += bytewise_equal(usize_load_unchecked(haystack, haystack.len() - chunksize), needles) & mask;
}
count += sum_usize(counts);

count
}
}

pub fn chunk_num_chars(utf8_chars: &[u8]) -> usize {
let chunksize = mem::size_of::<usize>();
assert!(utf8_chars.len() >= chunksize);

unsafe {
let mut offset = 0;
let mut count = 0;

// 2040
while utf8_chars.len() >= offset + chunksize * 255 {
let mut counts = 0;
for _ in 0..255 {
counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, offset));
offset += chunksize;
}
count += sum_usize(counts);
}

// 8
let mut counts = 0;
for i in 0..(utf8_chars.len() - offset) / chunksize {
counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, offset + i * chunksize));
}
if utf8_chars.len() % 8 != 0 {
let mask = !(!0 >> ((utf8_chars.len() % chunksize) * 8));
counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, utf8_chars.len() - chunksize)) & mask;
}
count += sum_usize(counts);

count
}
}

0 comments on commit c6668a3

Please sign in to comment.