Skip to content

okhttp: avoid unnecessary Header allocation when indexing already-lowercase names in Hpack.Writer - #2

Draft
jaipilot[bot] wants to merge 1 commit into
jaipilot-demo/grpc-12907from
jaipilot/pr-1-iwxT2TZEkHEP
Draft

okhttp: avoid unnecessary Header allocation when indexing already-lowercase names in Hpack.Writer#2
jaipilot[bot] wants to merge 1 commit into
jaipilot-demo/grpc-12907from
jaipilot/pr-1-iwxT2TZEkHEP

Conversation

@jaipilot

@jaipilot jaipilot Bot commented Aug 22, 2026

Copy link
Copy Markdown

What

Builds on grpc#12907 ("okhttp: optimize HPACK to index :path and fix dynamic table eviction bugs"), which fixed a bug where headers were inserted into the HPACK dynamic table using their original-case name instead of the lowercased name used for indexed lookups, by unconditionally allocating new Header(name, value) at both dynamic-table-insertion call sites in Writer.writeHeaders.

This change avoids that allocation in the common case: ByteString.toAsciiLowercase() returns this (same reference) when the name has no uppercase ASCII bytes, which is true for gRPC's generated header names and the newly-indexable :path/:authority pseudo headers. A small canonicalHeader(header, name, value) helper reuses the original Header instance when name == header.name, and otherwise allocates exactly as before (preserving the original PR's case-folding fix for mixed-case names).

Why

Header.equals/hashCode/hpackSize are purely value-based (over name/value content), and Header is immutable, so reusing the original instance when its name is already the indexing name is behavior-preserving: identical bytes are written, identical dynamic table state (size, order, byte accounting) results, and identical case-insensitive matching semantics apply.

Evidence

  • Behavior lock: added HpackTest#mixedCaseHeaderNameIndexedOnRepeat, verifying a mixed-case header is stored lowercase and recognized as an indexed reference on repeat. Passes identically before and after the change (59/59 HpackTest tests both times).
  • Performance: an instrumented allocation counter showed writeHeaders allocating 5 Header objects (one per header) on the original code for a representative gRPC header block, versus 0 on the optimized code, reproduced across multiple runs. A ThreadMXBean allocated-bytes benchmark (5 runs x 200k iterations each side) showed a consistent 8488 -> 8368 bytes/iteration reduction.
  • Full grpc-okhttp module build (compile, test, checkstyle) passes: 360 tests, 0 failures/errors.

Limitations

  • The performance benchmark was a standalone, non-committed microbenchmark (not part of this diff); only the deterministic allocation-count result and its methodology are reported here.
  • No change to public API, wire format, or dynamic-table semantics; scope limited to the two Writer.writeHeaders call sites and the eviction/indexing logic already touched by the evaluated PR.

Generated by JAIPilot Cloud for #1 from Anthropic session sesn_01758uGvBonuiwxT2TZEkHEP.

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.

0 participants