Skip to content

Optimize h2 parsers - #15676

Closed
lorban wants to merge 5 commits into
jetty-12.1.xfrom
enhancement/12.1/wendigo-h2-parsers
Closed

lorban wants to merge 5 commits into
jetty-12.1.xfrom
enhancement/12.1/wendigo-h2-parsers

Conversation

@lorban

@lorban lorban commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

The H2 parsers part of #15498 made by @wendigo

@lorban lorban self-assigned this Aug 26, 2026
@lorban lorban moved this to 🏗 In progress in FROZEN Jetty 12.1.13 Aug 26, 2026
HpackBenchmark measures HPACK alone, which left the obvious question
unanswered: how much of handling a frame is HPACK, and how much is
everything else. Nothing covered the parser and generator layers.

Add generation and parsing of whole HEADERS and DATA frames, warmed the
way a connection warms them, alongside a cold HEADERS parse.

The answer for parsing is that HPACK is essentially all of it: a warmed
HEADERS frame parses in about the same time HPACK alone takes to decode
it, and a 1 KiB DATA frame parses in under 30ns because the body is
sliced rather than copied.
@lorban
lorban force-pushed the enhancement/12.1/wendigo-h2-parsers branch from d50e4c7 to 178c380 Compare August 31, 2026 09:18
lorban and others added 4 commits August 31, 2026 11:45
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
HeaderParser stepped a 5-state machine once per octet to parse the
9 octets frame header, which is on the hot path of every frame.

When the whole header is available in the buffer (the common case),
read octets 0..7 with a single getLong() and extract length, type,
flags and the high 3 octets of the stream id with shifts and masks,
then read the last octet of the stream id. The octet-at-a-time state
machine is kept as the fallback for headers split across buffers.

This mirrors the existing 64-bit lookahead used by HttpParser for the
HTTP/1 request line.
PrefaceParser compared the 24 preface octets one at a time against
PREFACE_BYTES. Precompute the preface as overlapping 64-bit words so
that, when all the remaining preface octets are in the buffer, they
can be compared with 3 long comparisons instead of 24 byte ones.

The words are indexed by preface offset, so the fast path also covers
the h2c direct upgrade case, where the parser resumes at the offset
after PREFACE_PREAMBLE_BYTES; there fewer than 8 octets remain and the
comparison stays octet-wise.

The octet-at-a-time loop remains for prefaces split across buffers.
Failure handling is factored into invalidPreface() and is unchanged:
the buffer is cleared and a PROTOCOL_ERROR is notified.
SettingsBodyParser split each 6 octets setting across the SETTING_ID
and SETTING_VALUE states, so every setting cost two loop iterations
plus the state transition between them.

When all 6 octets are available, read the identifier and the value
together and stay in SETTING_ID for the next setting. The split-buffer
states are unchanged and still handle settings that straddle buffers.
@lorban

lorban commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor Author

I've reworked the benchmark to make it focus on all changes in this PR (and only changes made in this PR) and the perf improvements are not conclusive:

12.1.x vanilla

Benchmark                                   Mode  Cnt   Score   Error  Units
Http2FrameBenchmark.parseHeadersFrameCold   avgt    5  10.687 ± 0.126  ns/op
Http2FrameBenchmark.parseHeadersFrameWarm   avgt    5   2.005 ± 0.013  ns/op
Http2FrameBenchmark.parsePreface            avgt    5  30.059 ± 0.086  ns/op
Http2FrameBenchmark.parseSettingsFrameCold  avgt    5  20.579 ± 0.223  ns/op
Http2FrameBenchmark.parseSettingsFrameWarm  avgt    5   8.170 ± 0.160  ns/op

with PR

Benchmark                                   Mode  Cnt   Score   Error  Units
Http2FrameBenchmark.parseHeadersFrameCold   avgt    5  11.019 ± 0.273  ns/op
Http2FrameBenchmark.parseHeadersFrameWarm   avgt    5   9.273 ± 0.071  ns/op
Http2FrameBenchmark.parsePreface            avgt    5   7.454 ± 0.020  ns/op
Http2FrameBenchmark.parseSettingsFrameCold  avgt    5  20.302 ± 0.311  ns/op
Http2FrameBenchmark.parseSettingsFrameWarm  avgt    5   8.134 ± 0.068  ns/op

There is an improvement in the preface parser, but none in the settings parser and s noticeable degradation in the headers parser.

@lorban
lorban force-pushed the enhancement/12.1/wendigo-h2-parsers branch from 178c380 to 7bf195b Compare August 31, 2026 10:01
@sbordet sbordet added the Sponsored This issue affects a user with a commercial support agreement label Aug 31, 2026
@sbordet sbordet moved this to 👀 In review in Jetty 12.1.14 Aug 31, 2026
@lorban

lorban commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

Not conclusive perf improvements, closing without merging.

@lorban lorban closed this Sep 24, 2026
@lorban
lorban deleted the enhancement/12.1/wendigo-h2-parsers branch September 24, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Performance Sponsored This issue affects a user with a commercial support agreement

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

3 participants