<i18n dev> RFR: 8335252: ForceInline j.u.Formatter.Conversion#isValid [v2]
Claes Redestad
redestad at openjdk.org
Fri Jun 28 10:25:22 UTC 2024
On Thu, 27 Jun 2024 14:12:36 GMT, Shaojin Wen <duke at openjdk.org> wrote:
>> Currently, the java.util.Formatter$Conversion::isValid method is implemented based on switch, which cannot be inlined because codeSize > 325. This problem can be avoided by implementing it with ImmutableBitSetPredicate.
>>
>> use `-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining` to see the master branch:
>>
>> @ 109 java.util.Formatter$Conversion::isValid (358 bytes) failed to inline: hot method too big
>>
>>
>> current version
>>
>> @ 109 java.util.Formatter$Conversion::isValid (10 bytes) inline (hot)
>> @ 4 jdk.internal.util.ImmutableBitSetPredicate$SmallImmutableBitSetPredicate::test (50 bytes) inline (hot)
>
> Shaojin Wen has updated the pull request incrementally with one additional commit since the last revision:
>
> revert & use `@ForceInline`
So the compound issue here is that there's a heuristic which prevents inlining of large methods, and that a tableswitch on inputs spanning from `'%' (37)` to `'x' (120)` become expansive enough (350bytes) to make the JIT enforce that rule.
As an alternative to `@ForceInline` I tested splitting uppercase and lowercase chars into two switches. This looks a bit nasty, but actually reduces the size of the method:
diff --git a/src/java.base/share/classes/java/util/Formatter.java b/src/java.base/share/classes/java/util/Formatter.java
index a7d95ee5780..50419f1ea23 100644
--- a/src/java.base/share/classes/java/util/Formatter.java
+++ b/src/java.base/share/classes/java/util/Formatter.java
@@ -4947,30 +4947,36 @@ static class Conversion {
static final char PERCENT_SIGN = '%';
static boolean isValid(char c) {
- return switch (c) {
- case BOOLEAN,
- BOOLEAN_UPPER,
- STRING,
- STRING_UPPER,
- HASHCODE,
- HASHCODE_UPPER,
- CHARACTER,
- CHARACTER_UPPER,
- DECIMAL_INTEGER,
- OCTAL_INTEGER,
- HEXADECIMAL_INTEGER,
- HEXADECIMAL_INTEGER_UPPER,
- SCIENTIFIC,
- SCIENTIFIC_UPPER,
- GENERAL,
- GENERAL_UPPER,
- DECIMAL_FLOAT,
- HEXADECIMAL_FLOAT,
- HEXADECIMAL_FLOAT_UPPER,
- LINE_SEPARATOR,
- PERCENT_SIGN -> true;
- default -> false;
- };
+ if (c >= 'a') {
+ return switch (c) {
+ case BOOLEAN,
+ STRING,
+ HASHCODE,
+ CHARACTER,
+ DECIMAL_INTEGER,
+ OCTAL_INTEGER,
+ HEXADECIMAL_INTEGER,
+ SCIENTIFIC,
+ GENERAL,
+ DECIMAL_FLOAT,
+ HEXADECIMAL_FLOAT,
+ LINE_SEPARATOR -> true;
+ default -> false;
+ };
+ } else {
+ return switch (c) {
+ case BOOLEAN_UPPER,
+ STRING_UPPER,
+ HASHCODE_UPPER,
+ CHARACTER_UPPER,
+ HEXADECIMAL_INTEGER_UPPER,
+ SCIENTIFIC_UPPER,
+ GENERAL_UPPER,
+ HEXADECIMAL_FLOAT_UPPER,
+ PERCENT_SIGN -> true;
+ default -> false;
+ };
+ }
}
// Returns true iff the Conversion is applicable to all objects.
`javap -v java.lang.Formatter$Conversion`
static boolean isValid(char);
descriptor: (C)Z
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: iload_0
1: bipush 97
3: if_icmplt 122
6: iload_0
7: tableswitch { // 97 to 120
97: 116
98: 116
99: 116
100: 116
101: 116
102: 116
103: 116
104: 116
105: 120
106: 120
107: 120
108: 120
109: 120
110: 116
111: 116
112: 120
113: 120
114: 120
115: 116
116: 120
117: 120
118: 120
119: 120
120: 116
default: 120
}
116: iconst_1
117: goto 121
120: iconst_0
121: ireturn
122: iload_0
123: lookupswitch { // 9
37: 204
65: 204
66: 204
67: 204
69: 204
71: 204
72: 204
83: 204
88: 204
default: 208
}
204: iconst_1
205: goto 209
208: iconst_0
209: ireturn
```
And sees an improvement similar to `@ForceInline` on the micro:
```
Name Cnt Base Error Test Error Unit Change
stringIntFormat 15 92,447 ± 2,497 82,912 ± 3,317 ns/op 1,11x (p = 0,000*)
* = significant
```
Shrinking the size of the .class file by 121 bytes is a fun bonus.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/19926#issuecomment-2196585088
More information about the i18n-dev
mailing list