16

Possible Duplicate:
Should I use Java's String.format() if performance is important?

I was wondering if is good to use String.format in Java apps instead of StringBuilder... so, I just write a simple test, like this:

public static void main(String[] args) {
        int i = 0;
        Long start = System.currentTimeMillis();
        while (i < 10000) {
            String s = String.format("test %d", i);
            i++;
        }
        System.out.println(System.currentTimeMillis() - start);
        i = 0;
        start = System.currentTimeMillis();
        while (i < 10000) {
            String s = new StringBuilder().append("test ").append(i).toString();
            i++;
        }
        System.out.println(System.currentTimeMillis() - start);
    }

And the results where:

238
15

So, if my test is valid, StringBuilder is faster than String.format. OK. Now, I start thinking how String.format works. Is it a simple String concatenation, like "test " + i?

What the differences between StringBuilder concatenation and String.format? Is there a way simple as String.format and fast like StringBuilder?

Community
  • 1
  • 1
caarlos0
  • 20,020
  • 27
  • 85
  • 160
  • 2
    One important assumption in your post is: *"if my test is valid"* ;-) You should read about [how to create a micro benchmark in Java](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java). – assylias Oct 08 '12 at 17:53
  • 1
    Remember that the first case is actually `String.format("test %d", new Object[]{new Integer(i)})` which may account for a significant proportion of the overhead if the compiler isn't sufficiently clever. – tc. Oct 08 '12 at 18:05
  • 1
    `String.format` has to parse the format string each time, then do some fairly dynamic stuff to convert the arguments. Approaches using `StringBuilder` don't; the sequence of actions needed to build the string comes from the bytecode. Parsing and effectively executing a format string is likely to be orders of magnitude string than executing code. – Tom Anderson Oct 08 '12 at 19:02
  • Why do you even compare those? Try writing something like `String.format("% 5.3f")` using StringBuilder, then you'll understand why it's slower. – Dmitry Avtonomov Mar 15 '18 at 19:15

3 Answers3

38

I wrote a quick caliper benchmark to compare String.format() vs. StringBuilder, StringBuffer, normal String + operator, String.replace() and String.concat() methods:

public class StringFormatBenchmark extends SimpleBenchmark {

    public void timeStringFormat(int reps) {
        while (--reps >= 0) {
            String s = String.format("test %d", reps);
        }
    }

    public void timeStringBuilder(int reps) {
        while (--reps >= 0) {
            String s = new StringBuilder("test ").append(reps).toString();
        }
    }

    public void timeStringBuffer(int reps) {
        while (--reps >= 0) {
            String s = new StringBuffer("test ").append(reps).toString();
        }
    }

    public void timeStringPlusOperator(int reps) {
        while (--reps >= 0) {
            String s = "test " + reps;
        }
    }

    public void timeReplace(int reps) {
        while (--reps >= 0) {
            String s = "test {}".replace("{}", String.valueOf(reps));
        }
    }

    public void timeStringConcat(int reps) {
        while (--reps >= 0) {
            String s = "test ".concat(String.valueOf(reps));
        }
    }

    public static void main(String[] args) {
        new Runner().run(StringFormatBenchmark.class.getName());
    }

}

The results follow (Java 1.6.0_26-b03, Ubuntu, 32 bits):

caliper2

Clearly String.format() is much slower (by an order of magnitude). Also StringBuffer is considerably slower than StringBuilder (as we were taught). Finally StringBuilder and String + operator are almost identical since they compile to very similar bytecode. String.concat() is a bit slower.

Also don't use String.replace() if simple concatenation is sufficient.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 1
    It would be nice if you added `"test ".concat(String.valueOf(reps))` to the benchmarks since the special case of two concatenation operands is more likely to compile into that form (no benefit of a builder for a single operation). – Marko Topolnik Oct 08 '12 at 18:18
  • thanks, very nice tests. – caarlos0 Oct 08 '12 at 18:21
  • @MarkoTopolnik: so I did, thanks for the tip, I updated my benchmark and results. Feel free to run the test on you machine. – Tomasz Nurkiewicz Oct 08 '12 at 20:10
  • 1
    I ran it (Java 7, OS X), and the gap between `concat` and `StringBuilder` is even wider. That surprises me, `concat` ostensibly does less work (for example, only one `char[]` allocation). The `+` operator is even in this case compiled using `StringBuilder` (this, of course, depends on the compiler used; I used Eclipse's). – Marko Topolnik Oct 08 '12 at 21:03
  • 2
    It's very important not to leap to hasty conclusions from the picture you created! Your test measures the time for **one** string operation! (The loops are only for test accuracy.) If you build a string in a loop, `StringBuffer` and `StringBuilder` are **much** faster than the `+` operator. Example result: The `+` operator may take more than a minute to build the a string of 500000 characters where `StringBu{ffer,ilder}` build the exact same string in less than 50ms! – steffen Apr 10 '14 at 11:06
  • @MarkoTopolnik This whole benchmark can be completely optimized away as the strings gets never used. Summing their lengths would help. – maaartinus Sep 14 '14 at 22:35
  • 1
    @steffen Right, but then you could do something like `builder.append(String.format(...)` in order to get both readability and some speed. Appending to a string in a loop is indeed not an option. – maaartinus Sep 14 '14 at 22:38
3

String.format is relatively slower but usually more than fast enough.

I would use format if that is simpler, unless you can see a performance issue when you profile your application.

Note: The String.format in your example is taking ~24 micro-seconds and won't be fully warmed up yet. I would ignore the first 10K iterations.

IMHO "test " + i is the simplest in this case.

Ken
  • 30,811
  • 34
  • 116
  • 155
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

I have run a test post-JVM warmup (once the methods are compiled) and get similar results, with StringBuilder more than 30x quicker.

format: 943
stringbuilder: 26

public class TestPerf {

    private static int NUM_RUN;


    public static void main(String[] args) {
        NUM_RUN = 100_000;
        //warm up
        for (int i = 0; i < 10; i++) {
            method1();
            method2();
        }

        System.gc();
        System.out.println("Starting");

        long sum = 0;
        long start = System.nanoTime();
        for (int i = 0; i < 10; i++) {
            sum += method1();
        }
        long end = System.nanoTime();
        System.out.println("format: " + (end - start) / 1000000);

        System.gc();

        start = System.nanoTime();
        for (int i = 0; i < 10; i++) {
            sum += method2();
        }
        end = System.nanoTime();
        System.out.println("stringbuilder: " + (end - start) / 1000000);

        System.out.println(sum);
    }

    private static int method1() {
        int sum = 0;
        for (int i = 0; i < NUM_RUN; i++) {
            String s = String.format("test %d", i);
            sum += s.length();
        }
        return sum;
    }

    private static int method2() {
        int sum = 0;
        for (int i = 0; i < NUM_RUN; i++) {
            String s = new StringBuilder().append("test ").append(i).toString();
            sum += s.length();
        }
        return sum;
    }
}
assylias
  • 321,522
  • 82
  • 660
  • 783