Skip to content

Commit a8aaaee

Browse files
Christian CygnusChristian Cygnus
authored andcommitted
Optimize mergeInternal generation with specialized methods per subtype
Prior to this commit, our mergeInternal function that is created when a derived class is used as an Immutables target would check the possible types in a series of if blocks. However, this can lead to an explosion in the length of this method, especially for target classes with multiple parents. Long methods are concerning because it can exceed the JVM's default 8 KB limit for method compilation, forcing calls to remain interpreted. Instead, generate specialized methods for each relevant subtype. This helps break up the monolithic mergeInternal method while not introducing too many method hops. This approach, however, requires special handling of our bit masks, since we may have more than one long value to represent cases where the model has >64 fields. To deal with this, introduce a specialized case where we use a long[] of values, in order for each specialized method to easily update the overall state. This approach balances optimizing the simple case (<= 64 fields) for performance on small models, while allowing for larger models with only introducing a minimal amount of overhead in the allocation and access of the array values.
1 parent 2f03181 commit a8aaaee

6 files changed

Lines changed: 279 additions & 17 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package nonimmutables.huge;
2+
3+
import org.immutables.value.Value;
4+
5+
@Value.Immutable
6+
public interface ChildOfHugeParents extends HugeParentOne, HugeParentTwo {
7+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package nonimmutables.huge;
2+
3+
public interface HugeParentOne {
4+
int getFirst();
5+
6+
int getSecond();
7+
8+
int getThird();
9+
10+
int getFourth();
11+
12+
int getFifth();
13+
14+
int getSixth();
15+
16+
int getSeventh();
17+
18+
int getEighth();
19+
20+
int getNinth();
21+
22+
int getTenth();
23+
24+
int getEleventh();
25+
26+
int getTwelfth();
27+
28+
int getThirteenth();
29+
30+
int getFourteenth();
31+
32+
int getFifteenth();
33+
34+
int getSixteenth();
35+
36+
int getSeventeenth();
37+
38+
int getEighteenth();
39+
40+
int getNineteenth();
41+
42+
int getTwentieth();
43+
44+
int getTwentyFirst();
45+
46+
int getTwentySecond();
47+
48+
int getTwentyThird();
49+
50+
int getTwentyFourth();
51+
52+
int getTwentyFifth();
53+
54+
int getTwentySixth();
55+
56+
int getTwentySeventh();
57+
58+
int getTwentyEighth();
59+
60+
int getTwentyNinth();
61+
62+
int getThirtieth();
63+
64+
int getThirtyFirst();
65+
66+
int getThirtySecond();
67+
68+
int getThirtyThird();
69+
70+
int getThirtyFourth();
71+
72+
int getThirtyFifth();
73+
74+
int getThirtySixth();
75+
76+
int getThirtySeventh();
77+
78+
int getThirtyEighth();
79+
80+
int getThirtyNinth();
81+
82+
int getFortieth();
83+
84+
int getFortyFirst();
85+
86+
int getFortySecond();
87+
88+
int getFortyThird();
89+
90+
int getFortyFourth();
91+
92+
int getFortyFifth();
93+
94+
int getFortySixth();
95+
96+
int getFortySeventh();
97+
98+
int getFortyEighth();
99+
100+
int getFortyNinth();
101+
102+
int getFiftieth();
103+
104+
int getFiftyFirst();
105+
106+
int getFiftySecond();
107+
108+
int getFiftyThird();
109+
110+
int getFiftyFourth();
111+
112+
int getFiftyFifth();
113+
114+
int getFiftySixth();
115+
116+
int getFiftySeventh();
117+
118+
int getFiftyEighth();
119+
120+
int getFiftyNinth();
121+
122+
int getSixtieth();
123+
124+
int getSixtyFirst();
125+
126+
int getSixtySecond();
127+
128+
int getSixtyThird();
129+
130+
int getSixtyFourth();
131+
132+
int getSixtyFifth();
133+
134+
int getSixtySixth();
135+
136+
int getSixtySeventh();
137+
138+
int getSixtyEighth();
139+
140+
int getSixtyNinth();
141+
142+
int getSeventieth();
143+
144+
int getSeventyFirst();
145+
146+
int getSeventySecond();
147+
148+
int getSeventyThird();
149+
150+
int getSeventyFourth();
151+
152+
int getSeventyFifth();
153+
154+
int getSeventySixth();
155+
156+
int getSeventySeventh();
157+
158+
int getSeventyEighth();
159+
160+
int getSeventyNinth();
161+
162+
int getEightieth();
163+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package nonimmutables.huge;
2+
3+
public interface HugeParentTwo {
4+
String getAlpha();
5+
6+
String getBeta();
7+
8+
String getGamma();
9+
10+
String getDelta();
11+
12+
String getEpsilon();
13+
14+
String getZeta();
15+
16+
String getEta();
17+
18+
String getTheta();
19+
20+
String getIota();
21+
22+
String getKappa();
23+
24+
String getLambda();
25+
26+
String getMu();
27+
28+
String getNu();
29+
30+
String getXi();
31+
32+
String getOmicron();
33+
34+
String getPi();
35+
36+
String getRho();
37+
38+
String getSigma();
39+
40+
String getTau();
41+
42+
String getUpsilon();
43+
}

value-processor/src/org/immutables/value/processor/Immutables.generator

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,29 +1792,71 @@ public interface [tb.nameBuildFinal][type.generics] {
17921792
[/if]
17931793
private void mergeInternal(Object object) {
17941794
[dynamicFromModifiableCheck type 'object' 'return;']
1795-
[for l in bs.positions.longs]
1796-
[atVar type]long bits[emptyIfZero l.index] = 0;
1795+
[if bs.positions.hasMultipleLongs]
1796+
[atVar type]long['[]'] bits = new long['['][bs.positions.longs.size][']'];
1797+
[for s in bs.supertypes if s.attributes]
1798+
if (object instanceof [s.wildcard]) {
1799+
mergeFrom[toSafeIdentifier s.simpleName](bits, ([s.type]) object);
1800+
}
17971801
[/for]
1798-
[for s in bs.supertypes if s.attributes]
1802+
[else]
1803+
[atVar type]long bits = 0;
1804+
[for s in bs.supertypes if s.attributes]
17991805
if (object instanceof [s.wildcard]) {
1800-
[s.type] instance = ([s.type]) object;
1801-
[for v in s.attributes, BitPosition pos = bs.positions v.name]
1802-
[if pos]
1803-
if ((bits[emptyIfZero pos.index] & [literal.hex pos.mask]) == 0) {
1804-
[if v.nullableInSupertype]
1805-
[buildFromAttributeNullableSupertype v]
1806-
[else]
1807-
[buildFromAttribute v]
1808-
[/if]
1809-
bits[emptyIfZero pos.index] |= [literal.hex pos.mask];
1810-
}
1806+
bits = mergeFrom[toSafeIdentifier s.simpleName](bits, ([s.type]) object);
1807+
}
1808+
[/for]
1809+
[/if]
1810+
}
1811+
1812+
[-- Generate specialized merge methods per subtype --]
1813+
[for s in bs.supertypes if s.attributes]
1814+
[if bs.positions.hasMultipleLongs]
1815+
private void mergeFrom[toSafeIdentifier s.simpleName](long['[]'] bits, [s.type] instance) {
1816+
[-- Read array values into named variables --]
1817+
[for l in bs.positions.longs]
1818+
long bitMask[emptyIfZero l.index] = bits['['][l.index][']'];
1819+
[/for]
1820+
[for v in s.attributes, BitPosition pos = bs.positions v.name]
1821+
[if pos]
1822+
if ((bitMask[emptyIfZero pos.index] & [literal.hex pos.mask]) == 0) {
1823+
[if v.nullableInSupertype]
1824+
[buildFromAttributeNullableSupertype v]
18111825
[else]
18121826
[buildFromAttribute v]
18131827
[/if]
1814-
[/for]
1828+
bitMask[emptyIfZero pos.index] |= [literal.hex pos.mask];
18151829
}
1816-
[/for]
1830+
[else]
1831+
[buildFromAttribute v]
1832+
[/if]
1833+
[/for]
1834+
[-- Write named variables back to array --]
1835+
[for l in bs.positions.longs]
1836+
bits['['][l.index][']'] = bitMask[emptyIfZero l.index];
1837+
[/for]
18171838
}
1839+
[else]
1840+
private long mergeFrom[toSafeIdentifier s.simpleName](long bits, [s.type] instance) {
1841+
[for v in s.attributes, BitPosition pos = bs.positions v.name]
1842+
[if pos]
1843+
if ((bits & [literal.hex pos.mask]) == 0) {
1844+
[if v.nullableInSupertype]
1845+
[buildFromAttributeNullableSupertype v]
1846+
[else]
1847+
[buildFromAttribute v]
1848+
[/if]
1849+
bits |= [literal.hex pos.mask];
1850+
}
1851+
[else]
1852+
[buildFromAttribute v]
1853+
[/if]
1854+
[/for]
1855+
return bits;
1856+
}
1857+
[/if]
1858+
1859+
[/for]
18181860
[/for]
18191861
[else]
18201862

value-processor/src/org/immutables/value/processor/meta/FromSupertypesModel.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import javax.lang.model.element.TypeParameterElement;
4141
import javax.lang.model.type.TypeMirror;
4242
import javax.lang.model.util.ElementFilter;
43-
import javax.lang.model.util.Elements;
4443
import javax.lang.model.util.Types;
4544

4645
import org.immutables.generator.SourceTypes;
@@ -76,6 +75,10 @@ public final static class FromSupertype {
7675
this.attributes = ImmutableList.copyOf(attribute);
7776
}
7877

78+
public String simpleName() {
79+
return raw.substring(raw.lastIndexOf('.') + 1);
80+
}
81+
7982
@Override
8083
public String toString() {
8184
return type + " -> " + attributes;

value-processor/src/org/immutables/value/processor/meta/LongBits.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ public Collection<LongSet> longs() {
7979
return longPositions.values();
8080
}
8181

82+
public boolean hasMultipleLongs() {
83+
return longPositions.size() > 1;
84+
}
85+
8286
@Nullable
8387
@Override
8488
public BitPosition apply(Object input) {

0 commit comments

Comments
 (0)