Skip to content

perf: size decoded header maps from recent header blocks - #999

Open
patrick-immometrica wants to merge 1 commit into
hyperium:masterfrom
patrick-immometrica:decode-header-capacity-hint
Open

patrick-immometrica wants to merge 1 commit into
hyperium:masterfrom
patrick-immometrica:decode-header-capacity-hint

Conversation

@patrick-immometrica

Copy link
Copy Markdown

Follow-up to hyperium/http#886, where the suggestion was to keep a moving average in the parser instead of changing how HeaderMap grows.

Every HEADERS frame is decoded into a fresh HeaderMap::new(), so a request with 8-30 fields rehashes the map two or three times while it's being decoded. FrameDecoder now keeps a moving average of the field count of recent header blocks and creates the next block's map with that capacity. The newest block weighs 1/4 and the average rounds up, so a steady count doesn't decay. It's capped at 64 so one huge block doesn't make the following ones over-reserve. The first block on a connection is unchanged since the hint starts at 0.

I counted instructions with cachegrind (Linux, rust:1 container): 10k browser-like GET requests encoded with h2's own hpack encoder, so repeated headers hit the dynamic table like on a real connection, decoded through decode_frame. Numbers are per header block with the encode side subtracted.

fields master this PR change
4 4869 4896 +0.5%
8 9362 8476 -9.5%
12 12094 11207 -7.3%
16 15096 13478 -10.7%
20 18942 16475 -13.0%
30 30737 26173 -14.8%

Only HEADERS frames feed the average, PUSH_PROMISE just uses it. I added a unit test that checks the map is reserved before any field is decoded into it and that the hint stays at the cap.

Every HEADERS frame was decoded into a fresh HeaderMap::new(), so a
typical request of 8-30 fields rehashed the map two or three times
while decoding. FrameDecoder now keeps a moving average of the field
count of recent blocks and creates the next block's map with that
capacity. The first block on a connection is unchanged, and the hint
is capped at 64 so a single large block doesn't make later ones
over-reserve.

This branch has not been deployed

No deployments
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.

1 participant