You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 6124cb0
Browse filesBrowse the repository at this point in the historyBrowse files
Avoid intermediate slice views when reading token text
Lexing and parsing turn code point ranges into strings constantly: once for
every identifier, keyword and literal. Both call sites spelled this as
slice(i, j).toString(), which allocates an intermediate CelCodePointArray view
solely to copy out of it and then discard it.
Add CelCodePointArray.substring(i, j), which builds the String straight from the
backing array, and implement it in each of the four subclasses. toString()
becomes final and delegates to substring(0, size()), so the subclasses lose
their near-duplicate toString() overrides. Lexer.consumeIdent and
PrattParser.getTokenText call the new method.
This removes exactly one 32-byte object per token whose text is materialized.
Measured with CelParserBenchmark (parseOnly, built -c opt), comparing three
parsers back to back in one session: ANTLR, the Pratt parser before this change,
and the Pratt parser after it.
Objects allocated per parse:
| Case | ANTLR | Pratt before | Pratt after | Pratt vs ANTLR | Delta this CL |
| :--- | ---: | ---: | ---: | ---: | ---: |
| SMOKE_TEST | 357 | 123 | 120 | 3.0x smaller | -2.4% |
| CHAINED_ORS | 968 | 349 | 339 | 2.9x smaller | -2.9% |
| LIST_COMPREHENSION | 512 | 166 | 160 | 3.2x smaller | -3.6% |
| MESSAGE_CREATION | 1,253 | 426 | 406 | 3.1x smaller | -4.7% |
| LONG_LIST | 81,794 | 19,265 | 18,263 | 4.5x smaller | -5.2% |
Bytes allocated per parse:
| Case | ANTLR | Pratt before | Pratt after | Pratt vs ANTLR | Delta this CL |
| :--- | ---: | ---: | ---: | ---: | ---: |
| SMOKE_TEST | 12,256 | 3,608 | 3,512 | 3.5x smaller | -2.7% |
| CHAINED_ORS | 32,160 | 9,912 | 9,592 | 3.4x smaller | -3.2% |
| LIST_COMPREHENSION | 17,320 | 4,928 | 4,736 | 3.7x smaller | -3.9% |
| MESSAGE_CREATION | 43,128 | 13,056 | 12,416 | 3.5x smaller | -4.9% |
| LONG_LIST | 2,907,488 | 563,952 | 531,888 | 5.5x smaller | -5.7% |
Wall clock, mean of 3 caliper trial medians:
| Case | ANTLR | Pratt before | Pratt after | Pratt vs ANTLR | Delta this CL |
| :--- | ---: | ---: | ---: | ---: | ---: |
| SMOKE_TEST | 4,940 ns | 696 ns | 684 ns | 7.2x faster | -1.8% |
| CHAINED_ORS | 14,641 ns | 2,008 ns | 2,006 ns | 7.3x faster | -0.1% |
| LIST_COMPREHENSION | 7,514 ns | 1,250 ns | 1,217 ns | 6.2x faster | -2.7% |
| MESSAGE_CREATION | 20,979 ns | 3,526 ns | 3,514 ns | 6.0x faster | -0.4% |
| LONG_LIST | 1,616,631 ns | 140,500 ns | 146,640 ns | 11.0x faster | +4.4% |
Wall clock is unchanged within measurement noise. The per-case deltas run from
-2.7% to +4.4% and straddle zero, which is what a change that removes 3-5% of
allocations and no actual work should look like. The LONG_LIST row reads as a
regression, but that trial was noisy (per-trial medians 144.6us, 155.7us,
139.6us, against a much tighter 142.1us, 141.1us, 138.3us before) and its
fastest observed parse, 134.6us, is below the 135.7us baseline. The win here is
allocation volume and the GC pressure that follows from it. This is the first in
a series of parser changes; the wall-clock improvements come later in that
series.
The ANTLR column is included for scale, and shows why the Pratt parser exists.
ANTLR is slow enough on LONG_LIST that the case exceeds caliper's default 5
minute per-trial budget and has to be measured with a raised --time-limit.
PiperOrigin-RevId: 981290051
0 commit comments