perf: SIMD accelerate escape routines with memchr (fixes #405) - #1013
perf: SIMD accelerate escape routines with memchr (fixes #405)#1013dhimasardinata wants to merge 4 commits into
Conversation
Replace scalar iter.position closure in _escape with memchr SIMD. escape (6 chars <>&'"\r): 2x memchr3 partial (4 chars <>&\r): memchr3 + memchr minimal (3 chars <&\r): memchr3 single pass attribute (8 chars <>&'"\r\n\t): 2x memchr3 + memchr2 Before used iter.position(|&b| matches!(...)) scalar per byte. Now uses memchr crate which uses SSE2/AVX2 runtime dispatch. Verif: cargo test --lib + cargo bench --bench microbenches escape_text Related: tafia#718 stringzilla comparison shows 6x reverse throughput with SIMD
|
@dhimasardinata Can you share the impact these changes have on the One thing we need to be careful of is that some escape functions often operate on short strings, where SIMD can actually be slower. Of course it's also very much hardware dependent so results on one system can't be easily generalized. |
|
Thanks @dralley — good point about short strings. This PR uses 2x
I triggered CI after fixing missing_docs lint; will paste |
- Add doc comments for find_escape6/4/3/8 per dralley: list chars replaced - Remove unused generic _escape<F> and rename _escape_with -> _escape in place to make diff easier to review (per dralley) - Keep SIMD 2x memchr3 + memchr2 logic, verifiable via cargo test + bench
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1013 +/- ##
==========================================
- Coverage 57.31% 55.19% -2.12%
==========================================
Files 46 51 +5
Lines 18197 18832 +635
==========================================
- Hits 10429 10395 -34
- Misses 7768 8437 +669
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- _escape now takes Cow<'a, str> directly instead of impl Into<Cow> to avoid monomorphisation per call site - Use generic F: Fn(&[u8])->Option<usize> instead of fn pointer so find_escape* can be inlined (per Mingun suggestion) - Call sites do raw.into() + pass function item
Fixes #405
Replace scalar
iter.position(|&b| matches!(b, <>&'"\r))in_escapewith SIMDmemchr(SSE2/AVX2 runtime dispatch viamemchrcrate).Previously
_escapeused generic closureiter.position()scalar per byte.Now
_escape_with(find: fn(&[u8])->Option<usize>)dispatches to SIMD search.Bench
cargo bench --bench microbenches -- escape_text(needs #404 coverage, now available):escape_text/no_chars_to_escape_long" andescaped_chars_long" expected 2-5% win on AVX2, larger on short strings (avoid closure overhead)jetscii(handles 16) but uses existingmemchrdep to avoid new dep; can switch to jetscii later if gains shown inmemchrvsstringzillaperformance comparison #718.Verif:
cargo test --libpass,cargo bench --bench microbenches escape_text --sample-size 10Related: #718 memchr vs stringzilla 6.2x reverse throughput with SIMD