perf(scanner): cache per-thread direct ByteBuffers; add byte[] scan direct path#312
Open
XenoAmess wants to merge 7 commits into
Open
perf(scanner): cache per-thread direct ByteBuffers; add byte[] scan direct path#312XenoAmess wants to merge 7 commits into
XenoAmess wants to merge 7 commits into
Conversation
…irect path - Add ThreadLocal<ByteBuffer> scanBuffer and hasMatchBuffer - scan(String), hasMatch(String), hasMatch(byte[]) reuse cached direct ByteBuffers instead of allocating per call - Add scanRaw(db, byte[], handler) bypassing ByteBuffer.wrap() intermediate allocation
scanRaw previously allocated a new BytePointer (native malloc + full memcpy + JavaCPP deallocator registration) on every call, while hasMatch(byte[]) already reused a per-thread direct buffer. Non-empty input now goes through a cached rawScanBuffer (put() compiles to an Unsafe copy internally); null/empty input keeps the legacy path for zero behavioral change.
The zero-copy scan(Database, ByteBuffer, ...) path was private; only String and byte[] entry points were public, forcing users holding direct or memory-mapped buffers through a copying path. The new public overload scans direct buffers zero-copy and copies heap buffers into the reused per-thread rawScanBuffer; position/limit are left untouched. Includes direct/heap coverage in ScannerTest.
Match callbacks resolved expressions through HashMap.get on every match. Dense id spaces (auto-assigned or small custom ids) now build an Expression[] primary index; sparse id spaces fall back to the map. Same shape as the Panama wrapper's expressionsById.
encodeToBufferAndMap allocated a byte/short/int table sized by the (cached, ever-largest) buffer capacity on every String scan, even though the mapping is the identity for pure-ASCII input. The encoder now runs single-pass and only materializes a table when the first non-ASCII character is encountered, sized by the worst-case encoded length with an identity-backfilled ASCII prefix. Pure-ASCII scans return a shared IdentityByteCharMapping and allocate nothing. The Scanner callback previously treated mappingSize==0 as 'empty input'; it now always consults getCharIndex, which is the identity under the shared mapping, so reported indices are unchanged (verified by Utf8EncoderTest plus new ASCII/mixed cases).
- New Mode enum (BLOCK/STREAM/VECTORED); Database.compile gains mode-aware overloads, existing signatures keep BLOCK semantics. openStream/scanVector validate the database mode client-side (skipped for deserialized databases whose mode is unknown). - Scanner.openStream returns a Stream (Closeable): chunked scanning with cross-chunk matching, zero-copy for direct buffers, and a close(handler) variant that reports matches pending at end of stream (e.g. '$'-anchored patterns). - Scanner.scanVector accepts byte[][] and ByteBuffer[]; segments are matched as one logical input with offsets relative to the first segment. byte[][] and heap segments are bulk-packed into the reused per-thread direct buffer; direct segments are zero-copy. - StreamTest covers cross-boundary matching, close-time pending matches, use-after-close, vectored offsets, and mode validation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reduce per-call native memory allocation in Scanner's hot scan/hasMatch paths.
Changes
ThreadLocal<ByteBuffer> scanBuffer / hasMatchBuffer: , , and reuse cached direct ByteBuffers instead of calling
ByteBuffer.allocateDirect()on every invocation. Buffer is re-allocated only when the input is larger than the current capacity.byte[] scan direct path: New
scanRaw(Database, byte[], RawMatchEventHandler)usesBytePointer(byte[])directly, skipping the intermediateByteBuffer.wrap()allocation in the publicscan(Database, byte[], ByteMatchEventHandler)overload.Verification
Performance impact