Tom Copeland's Recent Posts

RSS Feeds

« Faster Java boolean inversions with XOR | Main | Fast String.indexOf() »

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83451d3c069e200e55070a51c8834

Listed below are links to weblogs that reference Even faster StringBuffer.append():

Comments

Are you saying its better to do String blah = "hello" + "world" ? Or do append("hello" + "world")?

Also, wouldn't thid depend heavily upon the JVM used?

he is saying that use sb.append("hello").append("world")

This code is actually in violation:

sb.append("hello").append("world")

Since you already know what can be done, you're better writing

sb.append("helloworld")

This code:
String blah = "hello" + "world"?
append("hello" + "world")?

Will actually be optimized by the compiler, where the first example won't.

Rob - Yup, the idea is to consolidate those two append calls into sb.append("hello world"). That'll eliminate the extra method call...

nec hit it on the head. He is saying:

sb.append("hello").append("world")

The example he gives isn't the best since he's using two literals. If they were variables (like in real world development) the statement would be:

sb.append(var1).append(var2);

instead of

sb.append(var1 + var2);

or

sb.append(var1);
sb.append(var2);

The append method returns the object "sb" just for this purpose.

Hi Jack - This rule won't flag consecutive appends that involve variables; it only triggers on literal appends. So the method chaining idiom should be OK... and I agree, it's quite handy.

What about a rule that covers an empty StringBuffer constructor followed by a literal append. Wouldn't

StringBuffer sb = new StringBuffer("helloworld")

save a few more cycles?

David - Yup, that's true, that would be another good rule; can you put in an RFE for it here?
https://sourceforge.net/tracker/?group_id=56262&atid=479924

> Are you saying its better to do String > blah = "hello" + "world" ?
> Or do append("hello" + "world")?

It's better to use '+' sign outside a loop:

String greeting = "Hello world" + " also known as " + theThirdPlanet;

Not only it's faster, but it's also easier to read. Only JDK 1.0 doesn't optimize that into StringBuffer with append.

More about StringBuffer Myth:

http://fishbowl.pastiche.org/2002/09/04/the_stringbuffer_myth

After I use jdk1.5, I leave this code;

String a = "Hello "+var1;

Hope jdk1.5 can optimized to use StringBuilder instead of StringBuffer.

It will better if StringBuilder does extends from StringBuffer, because some method that pass StringBuffer as argument will get benefits of StringBuilder incase of called in local thread.

From what I understand, under a modern JVM (Java 5+). StringBuilder is not actually any faster than StringBuffer in most cases. Although StringBuilder provides extra synchronisation, all that synchronisation is removed because the JVM knows when it is pointless. If synchronisation is needed (for the less than 1% of cases that you might use it in this way?) then you should be using StringBuffer instead of StringBuilder anyway (unless synchronising on another object perhaps)! :)

Kieron - Could be. Any idea if anyone's done some benchmarks along these lines? I've been looking at adding StringBuilder usage to JavaCC if the JDK_VERSION flag is set to "1.5", and no sense doing that if it really won't help....

Hi Tom,

Okay, I wrote this program to try to figure it out. Generally I hate micro-benchmarks, but I think I have made the appropriate allowances to prevent the JVM from optimising out my code.

public class Test {

public static void main(String[] args) {
try { Thread.sleep(5000); } catch (InterruptedException e) { }
testBuilder();
testBuffer();
testBuilder();
testBuffer();
testBuilder();
testBuffer();
testBuilder();
testBuffer();
testBuilder();
testBuffer();
}

private static void testBuilder() {
final long start = System.nanoTime();
final StringBuilder sb = new StringBuilder();
for (int i=0; i<5000000; i++) {
sb.append("T ");
}
final long end = System.nanoTime();
System.out.println("Builder: " + (end-start)/1000000);
System.out.println(sb.toString().length());
}

private static void testBuffer() {
final long start = System.nanoTime();
final StringBuffer sb = new StringBuffer();
for (int i=0; i<5000000; i++) {
sb.append("T ");
}
final long end = System.nanoTime();
System.out.println("Buffer: " + (end-start)/1000000);
System.out.println(sb.toString().length());
}
}

I got some interesting results!

Under Java 6 I got:

Builder: 413
Buffer: 368
Builder: 359
Buffer: 369
Builder: 285
Buffer: 317
Builder: 300
Buffer: 313
Builder: 309
Buffer: 323

And under Java 5 I got:

Builder: 403
Buffer: 685
Builder: 309
Buffer: 669
Builder: 311
Buffer: 669
Builder: 315
Buffer: 672
Builder: 316
Buffer: 667

So, I guess what I heard was correct, but it is a Java 6 specific optimisation. I guess you should ask yourself if it is worth optimising something that is irrelevent for Java 6 and onwards anyway... My thought it is that it probably not, and will just complicate your code.

Kieron - Thanks for the benchmarking! Yup, we should probably disable this rule if PMD is being run on code that's intended to be run on a Java 1.6 VM.

Keiron - Oh wait, I see, we were talking about StringBuilder vs StringBuffer, not the PMD rule ConsecutiveLiteralAppends. Although, the performance boost ConsecutiveLiteralAppends gives you may well disappear in Java 1.6, too... haven't tested that.

Actually, I had a compiling error when putting all the .append() together. There are 506 .apppend(). The error happened under JDK 1.5.0_14-b03. The problem will not happen if use multiple lines. So use this technique carefully.

The error is: java.lang.StackOverflowErrorat com.sun.tools.javac.comp.Lower.visitApply(Lower.java:2414)at com.sun.tools.javac.tree.Tree$Apply.accept(Tree.java:813)at com.sun.tools.javac.comp.Lower.translate(Lower.java:1881)at com.sun.tools.javac.comp.Lower.visitSelect(Lower.java:3013)at com.sun.tools.javac.tree.Tree$Select.accept(Tree.java:987)

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been saved. Comments are moderated and will not appear until approved by the author. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

Comments are moderated, and will not appear until the author has approved them.