<i18n dev> RFR: 8335791: Speed ​​up j.u.Formatter & String.format

Shaojin Wen duke at openjdk.org
Fri Jul 5 18:42:44 UTC 2024


On Fri, 5 Jul 2024 15:40:24 GMT, Shaojin Wen <duke at openjdk.org> wrote:

> String.format is widely used, and improving its performance is very meaningful. This PR can significantly improve the performance of String.format. Sorry, there are many changes, which will take a lot of time. I hope you can review it patiently.
> 
> 
> Improved performance includes the following:
> 
> ## 1. Write directly during the parse process to reduce object allocation.
> 
> In the current Formatter implementation, some objects do not need to be allocated, such as:
> 
> 
> class Formatter {
>     public Formatter format(Locale l, String format, Object ... args) {
>     	List<FormatString> fsa = parse(format);
>     	// ...
>     }
> 
>     static List<FormatString> parse(String s) {
>     	ArrayList<FormatString> al = new ArrayList<>();
> 
>         while (i < max) {
>             int n = s.indexOf('%', i);
>             if (n < 0) {
>                 // 
>                 al.add(new FixedString(s, i, max));
>             }
>         }
>     }
> }
> 
> In the process of parsing, the content that is not a Specifier is directly appended without going through FixedString. By directly printing the parsed FormatString object, there is no need to construct a `List<FormatString> fsa` to store it.
> 
> ## 2. Fast path print
> Use specialized FormatString implementations for single-character and single-width specifiers to avoid calling the large FormatSpecifier#print method.
> 
> ## 3. String.format directly calls j.u.Formatter
> String.format directly calls j.u.Formatter via SharedSecrets to improve performance

The performance improvement in most scenarios is very significant.

The performance of complexFormat/decimalFormat scenes has decreased because the inline root has changed. I have a solution and will submit it after this PR is completed. The content is these two commits:

[split print decimal](https://github.com/wenshao/jdk/commit/e3a6fe17912b5c17901e1518e74c9ab9a7e0057f)

[refactor parser](https://github.com/wenshao/jdk/commit/0cc63265062d1480bc31e9010f91e83bc7d911f3)

Performance numbers under MacBook M1 Pro


make test TEST="micro:java.lang.StringFormat"



# baseline 982e3f0e14dd4e5d0d13c6c0fe49903a555f5f5e
-Benchmark                               Mode  Cnt    Score    Error  Units
-StringFormat.complexFormat              avgt   15  944.745 ? 70.806  ns/op
-StringFormat.decimalFormat              avgt   15  285.709 ? 14.579  ns/op
-StringFormat.intFormat                  avgt   15   60.176 ?  1.258  ns/op
-StringFormat.intFormatUtf16             avgt   15  124.083 ?  3.547  ns/op
-StringFormat.intHexFormat               avgt   15  251.195 ? 68.026  ns/op
-StringFormat.intHexFormatUtf16          avgt   15  126.030 ? 14.631  ns/op
-StringFormat.intHexUFormat              avgt   15  201.430 ?  7.106  ns/op
-StringFormat.intHexUFormatUtf16         avgt   15  137.988 ? 13.681  ns/op
-StringFormat.intIntFormat               avgt   15  131.670 ?  6.723  ns/op
-StringFormat.intIntFormatUtf16          avgt   15  127.279 ?  2.857  ns/op
-StringFormat.intOctalFormat             avgt   15  248.651 ? 67.083  ns/op
-StringFormat.intOctalFormatUtf16        avgt   15  115.897 ?  2.131  ns/op
-StringFormat.lineFormatUtf16            avgt   15  143.465 ? 12.187  ns/op
-StringFormat.lineFormat                 avgt   15   59.753 ?  0.598  ns/op
-StringFormat.stringFormat               avgt   15   62.188 ?  2.317  ns/op
-StringFormat.stringFormatUtf16          avgt   15  158.438 ?  8.377  ns/op
-StringFormat.stringIntFormat            avgt   15  123.869 ?  4.107  ns/op
-StringFormat.stringIntFormatUtf16       avgt   15  173.503 ? 19.792  ns/op
-StringFormat.stringIntHexFormat         avgt   15  156.958 ?  6.641  ns/op
-StringFormat.stringIntHexUFormat        avgt   15  163.373 ?  9.474  ns/op
-StringFormat.stringIntOctalFormat       avgt   15  157.360 ?  6.569  ns/op
-StringFormat.stringIntOctalFormatUtf16  avgt   15  147.697 ?  6.050  ns/op
-StringFormat.stringIntRFormat           avgt   15  137.308 ?  7.921  ns/op
-StringFormat.stringIntRFormatUtf16      avgt   15  199.334 ?  6.728  ns/op
-StringFormat.stringWidthIntFormat       avgt   15  148.663 ?  3.549  ns/op
-StringFormat.stringWidthIntFormatUtf16  avgt   15  138.486 ?  3.704  ns/op
-StringFormat.widthStringFormat          avgt   15   94.476 ? 21.884  ns/op
-StringFormat.widthStringFormatUtf16     avgt   15  130.753 ?  3.555  ns/op
-StringFormat.widthStringIntFormat       avgt   15  153.143 ?  9.967  ns/op
-StringFormat.widthStringIntFormatUtf16  avgt   15  162.539 ?  6.323  ns/op

# current 5d866bf17d96bd0f0e4545d7eee5912eda2e3a94
+Benchmark                               Mode  Cnt     Score    Error  Units
+StringFormat.complexFormat              avgt   15  1008.713 ? 86.458  ns/op
+StringFormat.decimalFormat              avgt   15   386.167 ? 76.843  ns/op
+StringFormat.intFormat                  avgt   15    19.628 ?  0.076  ns/op
+StringFormat.intFormatUtf16             avgt   15    25.678 ?  0.224  ns/op
+StringFormat.intHexFormat               avgt   15   106.220 ? 31.809  ns/op
+StringFormat.intHexFormatUtf16          avgt   15   104.698 ?  7.862  ns/op
+StringFormat.intHexUFormat              avgt   15    99.228 ?  5.523  ns/op
+StringFormat.intHexUFormatUtf16         avgt   15    73.016 ?  5.311  ns/op
+StringFormat.intIntFormat               avgt   15    86.851 ?  3.479  ns/op
+StringFormat.intIntFormatUtf16          avgt   15    88.352 ?  4.553  ns/op
+StringFormat.intOctalFormat             avgt   15   176.610 ? 38.408  ns/op
+StringFormat.intOctalFormatUtf16        avgt   15    69.189 ?  1.362  ns/op
+StringFormat.lineFormat                 avgt   15    20.554 ?  0.044  ns/op
+StringFormat.lineFormatUtf16            avgt   15    45.253 ? 29.207  ns/op
+StringFormat.stringFormat               avgt   15    19.869 ?  0.073  ns/op
+StringFormat.stringFormatUtf16          avgt   15    25.119 ?  0.065  ns/op
+StringFormat.stringIntFormat            avgt   15    87.287 ?  2.808  ns/op
+StringFormat.stringIntFormatUtf16       avgt   15    76.377 ?  5.036  ns/op
+StringFormat.stringIntHexFormat         avgt   15   109.946 ? 53.831  ns/op
+StringFormat.stringIntHexUFormat        avgt   15   117.475 ? 55.030  ns/op
+StringFormat.stringIntOctalFormat       avgt   15   109.248 ?  5.347  ns/op
+StringFormat.stringIntOctalFormatUtf16  avgt   15   167.115 ? 25.219  ns/op
+StringFormat.stringIntRFormat           avgt   15    95.606 ?  8.299  ns/op
+StringFormat.stringIntRFormatUtf16      avgt   15    92.061 ? 17.822  ns/op
+StringFormat.stringWidthIntFormat       avgt   15    91.130 ?  5.954  ns/op
+StringFormat.stringWidthIntFormatUtf16  avgt   15   101.798 ?  7.317  ns/op
+StringFormat.widthStringFormat          avgt   15    20.644 ?  0.344  ns/op
+StringFormat.widthStringFormatUtf16     avgt   15    26.111 ?  0.228  ns/op
+StringFormat.widthStringIntFormat       avgt   15    79.138 ? 24.128  ns/op
+StringFormat.widthStringIntFormatUtf16  avgt   15    54.597 ?  1.191  ns/op


|   | baseline  | current | delta |
| --- | --- | --- | --- |
| StringFormat.complexFormat | 944.745 | 1008.713 | -6.34% |
| StringFormat.decimalFormat | 285.709 | 386.167 | -26.01% |
| StringFormat.intFormat | 60.176 | 19.628 | 206.58% |
| StringFormat.intFormatUtf16 | 124.083 | 25.678 | 383.23% |
| StringFormat.intHexFormat | 251.195 | 106.220 | 136.49% |
| StringFormat.intHexFormatUtf16 | 126.030 | 104.698 | 20.37% |
| StringFormat.intHexUFormat | 201.430 | 99.228 | 103.00% |
| StringFormat.intHexUFormatUtf16 | 137.988 | 73.016 | 88.98% |
| StringFormat.intIntFormat | 131.670 | 86.851 | 51.60% |
| StringFormat.intIntFormatUtf16 | 127.279 | 88.352 | 44.06% |
| StringFormat.intOctalFormat | 248.651 | 176.610 | 40.79% |
| StringFormat.intOctalFormatUtf16 | 115.897 | 69.189 | 67.51% |
| StringFormat.lineFormatUtf16 | 143.465 | 45.253 | 217.03% |
| StringFormat.lineFormat | 59.753 | 20.554 | 190.71% |
| StringFormat.stringFormat | 62.188 | 19.869 | 212.99% |
| StringFormat.stringFormatUtf16 | 158.438 | 25.119 | 530.75% |
| StringFormat.stringIntFormat | 123.869 | 87.287 | 41.91% |
| StringFormat.stringIntFormatUtf16 | 173.503 | 76.377 | 127.17% |
| StringFormat.stringIntHexFormat | 156.958 | 109.946 | 42.76% |
| StringFormat.stringIntHexUFormat | 163.373 | 117.475 | 39.07% |
| StringFormat.stringIntOctalFormat | 157.360 | 109.248 | 44.04% |
| StringFormat.stringIntOctalFormatUtf16 | 147.697 | 167.115 | -11.62% |
| StringFormat.stringIntRFormat | 137.308 | 95.606 | 43.62% |
| StringFormat.stringIntRFormatUtf16 | 199.334 | 92.061 | 116.52% |
| StringFormat.stringWidthIntFormat | 148.663 | 91.130 | 63.13% |
| StringFormat.stringWidthIntFormatUtf16 | 138.486 | 101.798 | 36.04% |
| StringFormat.widthStringFormat | 94.476 | 20.644 | 357.64% |
| StringFormat.widthStringFormatUtf16 | 130.753 | 26.111 | 400.76% |
| StringFormat.widthStringIntFormat | 153.143 | 79.138 | 93.51% |
| StringFormat.widthStringIntFormatUtf16 | 162.539 | 54.597 | 197.71% |

-------------

PR Comment: https://git.openjdk.org/jdk/pull/20055#issuecomment-2211096061


More information about the i18n-dev mailing list