Commit d34c5aa
perf(format): cache compact row layout per nested slot (#3717)
## Why?
Avoid redundant re-computation of compact codec offsets layout, saving
cpu and memory allocations
and
fix(format): honor input offset when copying inline-struct bytes
## What does this PR do?
perf(format): cache compact row layout per nested slot
Hoist offsets, widths, and nullability into CompactRowLayout, built
once per encoder. getStruct does no per-call lookup; inline-struct
reads go directly to the parent buffer; the writer reads schema-
derived state from the cache; compact rows skip the unused extData[]
allocation. For List<Struct> fields the parent layout also carries
the element layout so getArray(i) does not rebuild it.
Fix a latent inline-width bug in CompactBinaryRowWriter that was
masked by the prior slicing getStruct: writeUnaligned and
writeAlignedBytes dropped the input offset, so re-emitting a child
row via BinaryWriter.write(int, BinaryRow) copied the wrong bytes.
## Related issues
## AI Contribution Checklist
- [x] Substantial AI assistance was used in this PR: `yes` / `no`
AI Usage Disclosure
- substantial_ai_assistance: yes
- scope: all
- affected_files_or_subsystems: Java row format
- ai_review: ✔️
- ai_review_artifacts:
> One commit, perf(format): cache compact row layout per nested slot.
Caches fixedOffsets, per-field fixedWidths, allFieldsNotNullable, and
bitmapWidthInBytes in a new CompactRowLayout value, computed once per
array (element type is fixed) or memoized per parent extData slot.
> The getStruct hot path previously rebuilt fixedOffsets and re-walked
allNotNullable on every call.
> Recommendation: Approve. Numbers in the commit message match a real
bottleneck (the int[] + allNotNullable walk per nested decode), the
implementation preserves the existing wire behavior and ownership model,
no public API regression, all format tests pass, and the new caching
follows the existing extData-slot pattern rather than inventing a new
mechanism.
> — Reviewed by Claude (Opus 4.7), 2026-05-29
> Skill used: I ran this through the fory-code-review skill, which
delegated the diff inspection to a read-only review subagent loaded with
the Fory review checklist, the lesson-derived red flags, and the Java
runtime rules.
> Verdict
> No blockers. Recommend submission. Both commits are coherent and
tested:
> - d5cbdc2 is a real, narrow bugfix: the three put/copyFrom(..., 0,
numBytes) sites in CompactBinaryRowWriter were copying from the start of
the source buffer instead of from baseOffset. After schema sorting,
nested inline structs land at non-zero offsets, so this corrupted writes
for any non-trivial schema.
CompactCodecTest.testCopyInlineStructViaWriter exercises that exact
failure mode. Wire format unchanged — only the source byte range was
wrong.
> - 2b1825d precomputes a CompactRowLayout once per writer instead of
recomputing field offsets and nested layouts on every write. Cache is
per-CompactBinaryRowWriter instance, no map, no growth, no thread-safety
surface (writers are already not thread-safe). CompactRowLayoutTest pins
identity via assertSame at multiple nesting levels and covers the
allFieldsNotNullable bitmap-skip path. CompactBinaryRow.getStruct also
drops a per-call buffer.slice() allocation by pointing to the parent
buffer at the right offset instead.
- human_verification: ✔️
- performance_verification: ✔️
- provenance_license_confirmation: ✔️
## Does this PR introduce any user-facing change?
Compact codec user enjoys more CPU and memory on business logic without
even realizing it.
No public API or wire changes.
## Benchmark
```
RowFormatAllocationProbe bytes/op vs apache/main, compact mode,
median of five:
root 100208 -> 74305 (-25.9%)
array 9032 -> 6629 (-26.6%)
matrix 72005 -> 52760 (-26.7%)
map 7744 -> 6401 (-17.3%)
encode 55423 -> 41762 (-24.6%)
Standard mode unchanged within noise.
```
---------
Co-authored-by: Claude (on behalf of Steven Schlansker) <stevenschlansker+claude@apache.org>1 parent 7e52de7 commit d34c5aa
16 files changed
Lines changed: 433 additions & 130 deletions
File tree
- java/fory-format/src
- main/java/org/apache/fory/format
- encoder
- row/binary
- writer
- test/java/org/apache/fory/format
- encoder
- row/binary
Lines changed: 1 addition & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
33 | 32 | | |
34 | 33 | | |
35 | 34 | | |
| |||
38 | 37 | | |
39 | 38 | | |
40 | 39 | | |
41 | | - | |
42 | 40 | | |
43 | 41 | | |
44 | 42 | | |
45 | 43 | | |
46 | | - | |
47 | 44 | | |
48 | 45 | | |
49 | 46 | | |
| |||
82 | 79 | | |
83 | 80 | | |
84 | 81 | | |
85 | | - | |
| 82 | + | |
86 | 83 | | |
87 | 84 | | |
88 | 85 | | |
| |||
Lines changed: 0 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
27 | 26 | | |
28 | 27 | | |
29 | | - | |
30 | 28 | | |
31 | 29 | | |
32 | 30 | | |
| |||
76 | 74 | | |
77 | 75 | | |
78 | 76 | | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | 77 | | |
85 | 78 | | |
86 | 79 | | |
| |||
Lines changed: 0 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
27 | 26 | | |
28 | 27 | | |
29 | 28 | | |
| |||
72 | 71 | | |
73 | 72 | | |
74 | 73 | | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | 74 | | |
81 | 75 | | |
82 | 76 | | |
| |||
Lines changed: 0 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
27 | 26 | | |
28 | 27 | | |
29 | 28 | | |
| |||
47 | 46 | | |
48 | 47 | | |
49 | 48 | | |
50 | | - | |
51 | | - | |
52 | 49 | | |
53 | 50 | | |
54 | 51 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| |||
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
| 61 | + | |
61 | 62 | | |
62 | 63 | | |
| 64 | + | |
63 | 65 | | |
64 | 66 | | |
65 | 67 | | |
66 | | - | |
67 | 68 | | |
68 | 69 | | |
69 | 70 | | |
| |||
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
79 | 87 | | |
80 | 88 | | |
81 | 89 | | |
| |||
Lines changed: 25 additions & 18 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
28 | 27 | | |
29 | 28 | | |
30 | 29 | | |
| |||
33 | 32 | | |
34 | 33 | | |
35 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
36 | 39 | | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
37 | 45 | | |
38 | 46 | | |
39 | 47 | | |
40 | 48 | | |
41 | 49 | | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
42 | 59 | | |
43 | 60 | | |
44 | 61 | | |
| |||
105 | 122 | | |
106 | 123 | | |
107 | 124 | | |
| 125 | + | |
108 | 126 | | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
113 | 133 | | |
114 | | - | |
115 | | - | |
116 | 134 | | |
117 | 135 | | |
118 | 136 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | 137 | | |
131 | 138 | | |
132 | 139 | | |
| |||
Lines changed: 31 additions & 38 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
23 | | - | |
24 | 22 | | |
25 | 23 | | |
26 | 24 | | |
| |||
41 | 39 | | |
42 | 40 | | |
43 | 41 | | |
44 | | - | |
45 | | - | |
| 42 | + | |
46 | 43 | | |
47 | 44 | | |
48 | 45 | | |
49 | | - | |
| 46 | + | |
50 | 47 | | |
51 | 48 | | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
57 | 53 | | |
58 | 54 | | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
| 55 | + | |
| 56 | + | |
66 | 57 | | |
67 | 58 | | |
68 | 59 | | |
69 | 60 | | |
70 | | - | |
| 61 | + | |
71 | 62 | | |
72 | 63 | | |
73 | 64 | | |
74 | 65 | | |
75 | 66 | | |
76 | | - | |
77 | 67 | | |
78 | 68 | | |
79 | | - | |
| 69 | + | |
80 | 70 | | |
81 | 71 | | |
82 | 72 | | |
83 | 73 | | |
84 | | - | |
| 74 | + | |
85 | 75 | | |
86 | 76 | | |
87 | 77 | | |
| |||
94 | 84 | | |
95 | 85 | | |
96 | 86 | | |
97 | | - | |
| 87 | + | |
98 | 88 | | |
99 | 89 | | |
100 | 90 | | |
| |||
112 | 102 | | |
113 | 103 | | |
114 | 104 | | |
115 | | - | |
| 105 | + | |
| 106 | + | |
116 | 107 | | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
121 | 114 | | |
122 | | - | |
123 | | - | |
124 | 115 | | |
125 | 116 | | |
126 | 117 | | |
127 | 118 | | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
136 | 129 | | |
137 | 130 | | |
138 | 131 | | |
| |||
152 | 145 | | |
153 | 146 | | |
154 | 147 | | |
155 | | - | |
| 148 | + | |
156 | 149 | | |
157 | 150 | | |
0 commit comments