Add newline_delimited, slice_at, and skip documents by delimiter - #2803
Open
lemire wants to merge 2 commits into
Open
Add newline_delimited, slice_at, and skip documents by delimiter#2803lemire wants to merge 2 commits into
lemire wants to merge 2 commits into
Conversation
next_document() reaches the next document with skip_child(0), which loads the input byte behind every remaining structural and branches on it. When the caller reads only part of each document, that walk covers the rest of it. For a delimiter-framed stream the document ends at the next delimiter, so memchr plus a galloping search of the structural index gets there without touching the structurals in between. This applies to json_sequence and to a new stream_format::newline_delimited, where the caller guarantees one document per line. whitespace_delimited cannot use it: it permits several documents on one line and line feeds inside a document. The shortcut is gated on depth() > 0, which means the caller left the document part-read, so there is a walk worth avoiding, and the iterator is still inside the document, so the next delimiter terminates it. simdjson::slice_at cuts a padded_string_view into document-aligned slices. Documents are independent, so callers can parse the slices on as many threads as they like instead of relying on the built-in stage-1 thread, which caps out near a factor of two. doc/iterate_many.md shows the pattern. document_stream also allocated a stage1_worker, which holds a thread, a mutex and a condition variable, on every construction even with threading off. It is now allocated only when a stage-1 thread is started.
Move <cstring> outside SIMDJSON_CONDITIONAL_INCLUDE and use std::memchr so amalgamation always sees the declaration. Document stream_format::newline_delimited in iterate_many/parse_many docs and note that delimiter jumps skip structure-validation of unread document remainders under the one-line/RS contract.
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.
next_document()reaches the next document withskip_child(0), which loads the input byte behind every remaining structural and branches on it. When the caller reads only part of each document, that walk covers the rest of it.For a delimiter-framed stream the document ends at the next delimiter, so
memchrplus a galloping search of the structural index gets there without touching the structurals in between. This applies tojson_sequenceand to a newstream_format::newline_delimited, where the caller guarantees one document per line.whitespace_delimitedcannot use it: it permits several documents on one line and line feeds inside a document, so a line feed is a document boundary but not necessarily the next one.The shortcut is gated on
depth() > 0, which means the caller left the document part-read, so there is a walk worth avoiding, and the iterator is still inside the document, so the next delimiter terminates it.simdjson::slice_atcuts apadded_string_viewinto document-aligned slices. Documents are independent, so callers can parse the slices on as many threads as they like instead of relying on the built-in stage-1 thread, which caps out near a factor of two.doc/iterate_many.mdshows the pattern.document_streamalso allocated astage1_worker, which holds a thread, a mutex and a condition variable, on every construction even with threading off. It is now allocated only when a stage-1 thread is started.Benchmarks
benchmark/bench_stream_formats.cppnow runswhitespace_delimitedandnewline_delimitedside by side, plus abench_slicedvariant usingslice_atacross threads. On a 64-core Xeon Gold 6548N, small-document NDJSON:slice_at, 2 threadsslice_at, 4 threadsslice_at, 8 threadsTesting
131/131 tests pass on x86-64 (Xeon Gold 6548N, GCC 14) and 118/118 on arm64 (Apple Silicon, clang). New tests cover partial and full document reads, blank lines and CRLF, scalar documents, agreement with
whitespace_delimitedacross batch sizes, andslice_atreassembling its input exactly for a range of block sizes.Notes
document_streamnow befriendstoken_iteratorto reachpeek()andset_position(). Narrow accessors may be preferable.slice_atreturns an empty view when a block falls entirely inside one document, which happens only if that document is longer thanblock_size. Callers should iterate whileindex * block_size < sizeand skip empty slices rather than stopping at the first one.