Skip to content

perf: add rendered key byte cache in ByteRenderer - #1118

Draft
He-Pin wants to merge 2 commits into
databricks:masterfrom
He-Pin:perf/rendered-key-byte-cache
Draft

perf: add rendered key byte cache in ByteRenderer#1118
He-Pin wants to merge 2 commits into
databricks:masterfrom
He-Pin:perf/rendered-key-byte-cache

Conversation

@He-Pin

@He-Pin He-Pin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Motivation

Object keys are rendered (quoted + escaped) on every materialization. In K8s manifests and similar workloads, the same short keys (apiVersion, kind, metadata, spec) repeat thousands of times across objects. Each render re-runs getChars + escape scan unnecessarily.

Modification

  • 64-slot identity-keyed cache for quoted object key bytes in ByteRenderer
  • Cache hit: System.arraycopy pre-rendered bytes (zero escape scan)
  • Cache miss: render normally, populate slot if key.length <= 32
  • Keys > 64 chars bypass cache entirely (rare, not worth caching)
  • Identity-keyed (eq) so no hashCode/equals overhead on lookup

Result

Benchmark master this PR Delta
MainBenchmark.main 3.028 ± 0.536 ms/op 3.197 ± 1.924 ms/op within noise

JMH config: -f 2 -wi 5 -i 10 -w 1 -r 1

The stdlib.jsonnet benchmark has limited key repetition; the cache's primary benefit is in K8s-style manifests with high key reuse. Synergizes with identifier interning (#1117) which guarantees stable String identity for field names, maximizing cache hit rate.

All 424 tests pass.

Test plan

  • ./mill 'sjsonnet.jvm[_]'.test — all pass
  • ./mill bench.runJmh ".*MainBenchmark.*" — no regression

Motivation:
Object keys are rendered (quoted + escaped) on every materialization.
In K8s manifests and similar workloads, the same short keys ("apiVersion",
"kind", "metadata", "spec") repeat thousands of times across objects.
Each render re-runs getChars + escape scan unnecessarily.

Modification:
- 64-slot identity-keyed cache for quoted object key bytes
- Cache hit: System.arraycopy pre-rendered bytes (zero escape scan)
- Cache miss: render normally, populate if key.length <= 32
- Keys > 64 chars bypass cache entirely (rare, not worth caching)
- Identity-keyed (eq) so no hashCode/equals overhead on lookup

Result:
MainBenchmark: 3.028 → 3.197 ms/op (within noise ±1.9, no regression)
All 424 tests pass. The cache synergizes with identifier interning
(separate PR) which guarantees stable String identity for field names,
maximizing hit rate.
@He-Pin
He-Pin marked this pull request as draft August 12, 2026 05:34
Motivation:
Keys 33-64 chars passed the bypass check (> 64) but were never stored
(populate guard <= 32), causing wasted cache lookups on every render.

Modification:
Lower KeyCacheMaxKeyLength to 32 and remove the redundant populate
guard. Keys > 32 now bypass the cache entirely.

Result:
Eliminates dead lookup paths; simpler control flow with identical
caching behavior for keys <= 32.
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