Stream typed JSON lists without an adapter slice
perfloop/kubernetes · AVOIDABLE COPY
https://perfloop.ai/t/oss/case_azde8tk2g1
Verdict
VERIFIED · settled 2026-08-17
What happened: The paired measurements met the required improvement.
Hypothesis
The source constructs list := make([]runtime.Object, items.Len()) and fills it once per item. I traced its JSON consumer: json.getListMeta calls meta.ExtractList, returns that slice, streamEncodeCollections immediately passes it to encodeList, and encodeItemsObjectSlice only enumerates it sequentially. Thus the N-entry interface slice crosses the helper boundary and is not compiler-elided, but it is not reused by this JSON path. I ran `go test ./pkg/api/meta -run '^$' -bench '^BenchmarkExtractListItem$' -benchmem -count=1` from `staging/src/k8s.io/apimachinery`; its 1,000-item PointerReceiverList case measured 16,384 B/op and 1 alloc/op in ExtractList. The same fixture's value-receiver and RawExtension variants measured 1,001 allocs/op, but those fixture results do not establish production list mix or Serializer.Encode end-to-end cost. The proposed removed delta for the common pointer-item shape is the transient N-slot interface adapter (16,384 bytes in that 1,000-item check), not the item JSON payload or ownership of the original Items slice. A safe implementation must retain type/tag validation before bytes are written so invalid lists can still fall back, and must match extractList conversion semantics rather than blindly substituting EachListItem. Confirm independently with a Serializer.Encode benchmark over realistic typed lists at N=1, 100, and 1,000, checking encoded-byte equivalence plus nil/empty, RawExtension, and invalid-list cases; the expected signal is fewer bytes and allocations per list, with allocation/CPU profiles showing the removed adapter construction.
Change to test: Factor the item conversion in extractList into a non-collecting visitor usable by the JSON stream-list path, while retaining ExtractList for callers that require []runtime.Object. Have the JSON encoder emit each validated item directly, preserving nil-versus-empty Items, RawExtension Object/Raw handling, and fallback-before-write behavior.
Where it lives
perfloop/kubernetes · staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go
Evidence
Serializer.Encode streaming typed CarpList with 1 item · 10 sample pairs
| metric | baseline | candidate | paired median change | confidence range | required | result |
|---|---|---|---|---|---|---|
B/op |
624 |
608 |
−2.6% (−16) |
−16 to −16 |
≤ 0 |
PASSED |
allocs/op |
17 |
16 |
−5.9% (−1) |
−1 to −1 |
< −0.85 |
PASSED |
Serializer.Encode streaming typed CarpList with 100 items · 10 sample pairs
| metric | baseline | candidate | paired median change | confidence range | required | result |
|---|---|---|---|---|---|---|
B/op |
2407 |
614 |
−74.5% (−1793) |
−1793 to −1792 |
< −120.4 |
PASSED |
allocs/op |
17 |
16 |
−5.9% (−1) |
−1 to −1 |
≤ 0 |
PASSED |
Serializer.Encode streaming typed CarpList with 1000 items · 10 sample pairs
| metric | baseline | candidate | paired median change | confidence range | required | result |
|---|---|---|---|---|---|---|
B/op |
17478 |
1098 |
−93.7% (−16385) |
−16404 to −16372 |
< −873.9 |
PASSED |
allocs/op |
18 |
17 |
−5.6% (−1) |
−1 to −1 |
≤ 0 |
PASSED |
Checks: 1 of 1 passed. Verification: no defect found.
Timeline
2026-07-17· Case opened2026-07-21· Attempt selected