Speed up Slice search, UTF-8 case conversion, and trims - #196
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThe changes optimize forward and reverse slice searches with word-based candidate detection and anchor verification. UTF-8 case conversion and whitespace trimming now use paged lookup data and word-at-a-time scanning. Basic slice input uses explicit bounds checks with unchecked reads, while dynamic zero writing validates lengths and clears backing regions directly. Slice growth padding is bounded, and randomized and regression tests cover these behaviors. 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
toUpperCase and toLowerCase now scan and translate ASCII input eight bytes at a time by flipping the case bit of bytes in the cased range. This makes case conversion of ASCII inputs 3-9x faster at 100+ bytes while keeping non-ASCII inputs on the existing code-point path.
indexOf now scans eight-byte windows with an exact SWAR zero-byte mask, visiting candidate positions via bit iteration instead of re-reading memory, and rejects false candidates with head and tail anchors before the full comparison. This makes substring search 1.2-4x faster across sparse and repetitive inputs.
The exact SWAR mask has no false positives, so the highest set bit is the last occurrence and the per-candidate re-check loop is unnecessary. This makes matches near ambiguous bytes ~3x faster and backward scans ~20% faster.
readByte checks the position once and reads unchecked instead of going through read() and its -1 sentinel, which paid a second bounds check in the checked getByte. Semantics are unchanged, including the exception thrown at end of input.
writeZero now ensures capacity once and clears the range with a single Arrays.fill instead of looping writeLong(0), which re-entered ensureSize for every eight bytes. This makes writeZero about 2x faster.
The lower, upper, and title case tables are now 256-entry pages of deltas where every unmapped page shares a single zero page, shrinking the tables from 12.7 MB of flat arrays to about 150 KB (33-36 distinct pages each). Translation of uniformly random non-ASCII code points measures up to 10% slower due to the extra indirection, while ASCII paths are unchanged.
The whitespace table becomes 256-entry pages where pages without any whitespace share a single empty page, shrinking it from 1 MB to a few kilobytes. Trim and title-case behavior is unchanged and benchmarks are within noise.
lastIndexOf now compares the first and last four pattern bytes before running the full comparison, mirroring indexOf. Repetitive inputs whose candidates fail deep into the pattern search about 1.7x faster, while random inputs are unchanged.
Trim scans skip ASCII whitespace with a SWAR stop mask over the two contiguous whitespace ranges, and the forward scan skips homogeneous runs with the vectorized Arrays.mismatch. ASCII whitespace trims at 100+ bytes get 1.8-3.8x faster, while inputs of randomly mixed multi-byte whitespace measure up to 19% slower due to the unpredictable run detection.
7b32fa1 to
d1c0b9b
Compare
A batch of performance improvements to Slice and SliceUtf8, plus one bug fix. Each commit is independent and individually benchmarked (JMH, JDK 25, Apple Silicon; multi-fork runs where single-fork variance was significant).
Bug fix
Search
UTF-8
I/O