Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8050142

Optimize java.util.Formatter

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Fixed
    • Icon: P4 P4
    • 9
    • None
    • core-libs
    • None
    • b33
    • generic
    • generic

        As Martin Buchholz pointed out way back in JDK-6898220, java.util.Formatter is allocation intense and can be further optimized. Here are a few pretty straightforward things:

         - avoid converting data back and forth between String, StringBuilder and char[] (especially using String.toCharArray()), instead try to operate on StringBuilders as much as possible. I consider getting rid of System.arraycopy a nice cleanup, too.
         - regex: avoid using m.group on single-char groups, instead use m.start(idx)/m.end(idx) and charAt().
         - parse: return the ArrayList<FormatSpecifier> directly instead of converting it to FormatSpecifier[]; since the recipient simply iterates over the specifiers once, converting to array is wasteful
         - FixedString: constant string expressions were substrings, which since 7u6 copies the data. By instead saving the offset and rely on range-based append methods of StringBuilder a small speedup is achieved.

         The code can be further optimized using JDK-8041972 (parsing index, width, precision without intermediate substrings gains a few percent for complex cases) and perhaps JDK-8050114, but I will file separate RFEs for those.

        Internal microbenchmarks show speedups ranging from 10-30%; some examples (Intel(R) Core(TM) i5-3320M CPU @ 2.60GHz, Linux 3.13):

        Benchmark Mode Samples Score Score error Units
        old.FormatBench.formatDecimal thrpt 20 2120314.923 48250.799 ops/s
        new.FormatBench.formatDecimal thrpt 20 2739464.530 70673.303 ops/s # 1.29x

        old.FormatBench.formatHex thrpt 20 1741011.847 28234.553 ops/s
        new.FormatBench.formatHex thrpt 20 2172309.104 34826.348 ops/s # 1.25x

        old.FormatBench.formatDouble thrpt 20 1199099.261 40828.750 ops/s
        new.FormatBench.formatDouble thrpt 20 1334265.418 20479.704 ops/s # 1.11x

        old.FormatBench.formatString thrpt 20 2437233.293 45434.999 ops/s
        new.FormatBench.formatString thrpt 20 3183689.809 52892.969 ops/s # 1.30x

        old.FormatBench.formatComplex thrpt 20 489896.437 9006.345 ops/s
        new.FormatBench.formatComplex thrpt 20 578696.625 8039.759 ops/s # 1.18x

        old.FormatBench.formatScience thrpt 20 938680.776 11932.883 ops/s
        new.FormatBench.formatScience thrpt 20 1119664.074 14700.473 ops/s # 1.19x

        package org.sample;

        import org.openjdk.jmh.annotations.*;
        import java.math.BigInteger;

        @State(Scope.Thread)
        public class FormatBench {

            public int value = 4711;

            public int width = 6;

            public float pi = (float)Math.PI;

            public String str = "foo";

            @Benchmark
            public String formatDecimal() {
                return String.format("%d", value);
            }

            @Benchmark
            public String formatHex() {
                return String.format("%06x", value);
            }

            @Benchmark
            public String formatDouble() {
                return String.format("%.4f", pi);
            }

            @Benchmark
            public String formatScience() {
                return String.format("%5.5e", pi);
            }

            @Benchmark
            public String formatComplex() {
                return String.format(" %04x %05x %06x %05x", value, value, value, value);
            }

            @Benchmark
            public String formatString() {
                return String.format("%s", str);
            }

              redestad Claes Redestad
              redestad Claes Redestad
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: