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?
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);