<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