Skip to content

pxf: DatasetReader lexes a long token once, however many reads it spans (#168) - #172

Merged
trendvidia merged 1 commit into
mainfrom
dataset-scan-linear-168
Nov 7, 2026
Merged

trendvidia merged 1 commit into
mainfrom
dataset-scan-linear-168

Conversation

@bfrancojr

@bfrancojr bfrancojr commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #168. Built on #167 (#164), which brought in the lexer-based scan this changes. #167 has merged, and this PR is rebased onto main (531c586).

What was wrong

A token still open at the end of a 4 KiB read was lexed again from its start on every read. That covers an unclosed string, a bytes literal, a comment, or one long number or identifier. A token of n bytes cost about n²/8192 byte visits: 136 MB of lexing for 1 MiB, 540 MB for 2 MiB. The header scan lexed the whole buffer from the start on every read.

What changes

The reader now does what protowire-go's reader does (mayEndToken). The scan records the open token, and lexes it again only when the bytes read since could end it:

  • a closing quote not escaped by an odd run of backslashes;
  • a newline;
  • """ or */;
  • a byte that ends a run, for a timestamp;
  • a byte that cannot continue an identifier or number;
  • a bytes literal reaching max_bytes_literal_length.

A token under 8 bytes is always lexed again. The rule only decides when the lexer looks again, never what the token is. The header scan now keeps its place between reads too. An @proto body is matched by BraceMatch, a resumable form of FindMatchingBrace, which now wraps it.

Evidence

Bytes lexed, counted through a test peer (DatasetReaderTestPeer), for an unclosed row after the header, read 4 KiB at a time:

Shape 1 MiB, with this PR 1 MiB, waiting turned off 2 MiB, waiting turned off
unterminated string 1,052,659 135,786,499 540,008,451
one long number 1,052,658 135,786,498 540,008,450
line comment 1,052,660 135,785,990 540,007,430
one long bytes literal 1,052,660 135,786,500 540,008,452
short tokens (control) 1,048,834 1,048,834 2,097,666

The off figures fit n²/8192, which is 134 M for 1 MiB, and they grow 4× for 2× the input. All 10 shapes behave the same way: Go's 9, plus a long bytes literal. For a header near 64 KiB with one long string, 65,555 bytes are lexed against 552,853 with waiting off.

Not in this PR

Go's third test in the same file, CompleteRowIsNotHeldBack, needs a live stream, and cpp's Pull uses std::istream::read, which waits for a full 4 KiB. That's #171. Go's one-byte-reader variants of these tests have no cpp equivalent until #171, since every read is 4 KiB or the rest of the input.

Base automatically changed from dataset-row-end-164 to main November 7, 2026 04:21
@trendvidia
trendvidia force-pushed the dataset-scan-linear-168 branch from 2c84b66 to 3f3e817 Compare November 7, 2026 04:23
…ns (#168)

A token still open at the end of a read was lexed again from its start
on every 4 KiB read, so an unclosed string, bytes literal, comment or
number of n bytes cost about n*n/8192 byte visits: 136 MB for 1 MiB,
540 MB for 2 MiB. As protowire-go's reader does (mayEndToken), the scan
now records the open token and lexes it again only when the bytes read
since could end it: a closing quote not escaped by an odd run of
backslashes, a newline, `"""`, `*/`, a byte that ends a run, a byte that
cannot continue an identifier or number, or a bytes literal reaching
max_bytes_literal_length. A token under 8 bytes is always lexed again.

The header scan resumes too. It keeps its place in the header between
reads, and an @proto body is matched by a resumable BraceMatch, which
FindMatchingBrace now wraps.

Tests DatasetReaderScan.IsLinear and HeaderIsLinear count the bytes
lexed (through a test peer) for Go's shapes plus a long bytes literal,
at 1 and 2 MiB, and for headers near the 64 KiB budget. They allow
twice the input and see about 1.004x. With the waiting rule turned off,
18 of the 20 row cases and the long-string header case fail.
@trendvidia
trendvidia force-pushed the dataset-scan-linear-168 branch from 3f3e817 to 38a9ca2 Compare November 7, 2026 06:49
@trendvidia
trendvidia merged commit 42d3c27 into main Nov 7, 2026
11 checks passed
@trendvidia
trendvidia deleted the dataset-scan-linear-168 branch November 7, 2026 07:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DatasetReader re-lexes a long token from its start on every read

2 participants