Skip to content

Commit

Permalink
Replace the slow loop in Teddy with Aho-Corasick.
Browse files Browse the repository at this point in the history
This can have a significant impact on performance when the match rate is
low (common) and the number of patterns is high (e.g., for a case
insensitive search).
  • Loading branch information
BurntSushi committed Sep 4, 2016
1 parent 12cb63b commit 3239782
Showing 1 changed file with 12 additions and 35 deletions.
47 changes: 12 additions & 35 deletions src/simd_accel/teddy128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ References
use std::cmp;
use std::ptr;

use aho_corasick::{Automaton, AcAutomaton, FullAcAutomaton};
use simd::u8x16;
use simd::x86::sse2::Sse2Bool8ix16;
use simd::x86::ssse3::Ssse3U8x16;
Expand All @@ -354,6 +355,9 @@ pub struct Match {
pub struct Teddy {
/// A list of substrings to match.
pats: Vec<Vec<u8>>,
/// An Aho-Corasick automaton of the patterns. We use this when we need to
/// search pieces smaller than the Teddy block size.
ac: FullAcAutomaton<Vec<u8>>,
/// A set of 8 buckets. Each bucket corresponds to a single member of a
/// bitset. A bucket contains zero or more substrings. This is useful
/// when the number of substrings exceeds 8, since our bitsets cannot have
Expand Down Expand Up @@ -403,6 +407,7 @@ impl Teddy {
}
Some(Teddy {
pats: pats.to_vec(),
ac: AcAutomaton::new(pats.to_vec()).into_full(),
buckets: buckets,
masks: masks,
})
Expand Down Expand Up @@ -570,7 +575,7 @@ impl Teddy {

prev0 = res0;
prev1 = res1;

let bitfield = res.ne(zero).move_mask();
if bitfield != 0 {
let pos = pos.checked_sub(2).unwrap();
Expand Down Expand Up @@ -659,27 +664,13 @@ impl Teddy {
/// This is used when we don't have enough bytes in the haystack for our
/// block based approach.
fn slow(&self, haystack: &[u8], pos: usize) -> Option<Match> {
// TODO: Use Aho-Corasick, or otherwise adapt the block based approach
// to be capable of using smaller blocks.
let mut m = None;
for (pi, p) in self.pats.iter().enumerate() {
if let Some(i) = find_slow(p, &haystack[pos..]) {
let candidate = Match {
pat: pi,
start: pos + i,
end: pos + i + p.len(),
};
match m {
None => m = Some(candidate),
Some(ref mut m) => {
if candidate.start < m.start {
*m = candidate;
}
}
}
self.ac.find(&haystack[pos..]).next().map(|m| {
Match {
pat: m.pati,
start: pos + m.start,
end: pos + m.end,
}
}
m
})
}
}

Expand Down Expand Up @@ -802,17 +793,3 @@ impl UnsafeLoad for u8x16 {
x
}
}

/// Slow single-substring search use for naive brute force matching.
#[cold]
pub fn find_slow(pattern: &[u8], haystack: &[u8]) -> Option<usize> {
if pattern.len() > haystack.len() {
return None;
}
for i in 0..(haystack.len() - pattern.len() + 1) {
if pattern == &haystack[i..i + pattern.len()] {
return Some(i);
}
}
None
}

0 comments on commit 3239782

Please sign in to comment.