Search code examples
javaperformanceiostringbuilder

Java System.out.print efficiency


In the last few days, doing some competitive programming exercises, I found some solutions to some problems where instead of printing out text in the console in a for loop, they constructed a StringBuilder by appending the texts after each other. First I was confused but I gave it a try and made some benchmarks.

First Test: this is the classic way to print something on the screen:

class TestClass {

    public static void main(String args[]) throws Exception {
        double startTime = System.currentTimeMillis();

        //code
        for (int i = 0; i < 1000000; i++) {
            System.out.print("test");
        }
        //code
        double endTime = System.currentTimeMillis();
        String result = String.format("for loop: %.5f", (endTime-startTime) / 1000);
        System.out.print("\n"+result);
    }

}

Second test: and this is the enhanced version with StringBuilder:

class TestClass {

    public static void main(String args[]) throws Exception {
        double startTime = System.currentTimeMillis();

        //code
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 1000000; i++) {
            sb.append("test");
        }
        System.out.print(sb);
        //code
        double endTime = System.currentTimeMillis();
        String result = String.format("for loop: %.5f", (endTime - startTime) / 1000);
        System.out.print("\n" + result);
    }

}

After running the tests several times(running them with Intel i5 6500 quad core 3.2 GHZ CPU, Win 10), the results are the following:

first test(for loop): 4,25s  
second test(StringBuilder): 0.23s

The results are not unexpected because out.print is synchronized so every time we call it, it will add some overhead. But being almost 20 times slower is somewhat strange.

My question is: is there a way we can improve this further?


Solution

  • synchronized keyword should not be blamed in this case.

    The first test case is slow, because System.out.print will bring frequent minimal I/O.

    You can use BufferedWriter(it's write method is synchronized too) to reduce unecessary I/O:

    BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
    
    //code
    for (int i = 0; i < 1000000; i++) {
        log.write("test");
    }
    
    for loop: 0.21200
    

    And, StringBuilder use byte[] for character storage internally. This byte array will grow when you keep appending contents, by copying old value into new byte[].

    Give it a big enough capacity might bring some efficiency:

    StringBuilder sb = new StringBuilder(1000000 * 10);