-
Enhancement
-
Resolution: Fixed
-
P4
-
None
-
b21
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8319606 | 11.0.23-oracle | Ryan Wallace | P4 | Resolved | Fixed | b01 |
JDK-8322273 | 11.0.22.0.1-oracle | Dukebot | P4 | Resolved | Fixed | b01 |
JDK-8320048 | 11.0.21.0.2-oracle | Ryan Wallace | P4 | Closed | Fixed | b01 |
Bound MethodHandles uses generated Species classes to hold bound arguments and combinators, and a String concatenation expression can contain many such bindings. Most of the binding are of reference type, typically binding in method handles like the mixers and prependers. The odd one out is the binding in of the initialLengthCoder. When this is bound in, we extend a species like Species_LLLL (a Species with four references bound in) with an argument of type long (J) and get Species_LLLLJ. So in larger applications with a lot of String concat, you'll see Species types with a lot of L's and one J.
Instead we can bind in a constant method handle that provides the calculated value:
- mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder);
+ mh = MethodHandles.foldArguments(mh, 0, MethodHandles.constant(long.class, initialLengthCoder));
The result is that when we bind in the argument, we'll extend the species type with yet another reference (L) and go from Species_LLLL to Species_LLLLL. Other concat expressions and features built on MHs are likely to need Species_LLLLL but less likely to need Species_LLLLJ, and as these class are perfectly shareable this mean more reuse of internal structures.
Benchmark results are net-neutral on throughput, but on various startup benchmarks - including real-world desktop applications - this reduces the amount of Species classes generated and used significantly, speeding up startup by a modest amount.
--
While testing the above I realized that an even better solution would be to fold the initialLengthCoder into the last mixer we add into the combinator tree. This doesn't only reduce species types in the aggregate, but shortens trees and ultimately generate fewer LFs on all String concat startup tests and is a win already the first concatenation bootstrapped through this.
Instead we can bind in a constant method handle that provides the calculated value:
- mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder);
+ mh = MethodHandles.foldArguments(mh, 0, MethodHandles.constant(long.class, initialLengthCoder));
The result is that when we bind in the argument, we'll extend the species type with yet another reference (L) and go from Species_LLLL to Species_LLLLL. Other concat expressions and features built on MHs are likely to need Species_LLLLL but less likely to need Species_LLLLJ, and as these class are perfectly shareable this mean more reuse of internal structures.
Benchmark results are net-neutral on throughput, but on various startup benchmarks - including real-world desktop applications - this reduces the amount of Species classes generated and used significantly, speeding up startup by a modest amount.
--
While testing the above I realized that an even better solution would be to fold the initialLengthCoder into the last mixer we add into the combinator tree. This doesn't only reduce species types in the aggregate, but shortens trees and ultimately generate fewer LFs on all String concat startup tests and is a win already the first concatenation bootstrapped through this.
- backported by
-
JDK-8319606 Reduce String concatenation shapes by folding initialLengthCoder into last mixer
- Resolved
-
JDK-8322273 Reduce String concatenation shapes by folding initialLengthCoder into last mixer
- Resolved
-
JDK-8320048 Reduce String concatenation shapes by folding initialLengthCoder into last mixer
- Closed