Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this usage of concatentation in StringBuilder's constructor 100X faster than calling append()?

I stumbled upon a bizarre performance issue related to the StringBuilder's append method. I noticed what appeared to be a foolish mistake - string concatenation during the use of the StringBuilder constructor (this even shows up as a warning in the NetBeans IDE).

Version 1

int hash = -1;     //lazily computed, also present in Version 2
int blockID = ...  //0 to 1000, also present in Version 2
int threadID = ... //0 to 1000, also present in Version 2
boolean hashed = false;       //also present in Version 2

@Override
public int hashCode(){
    if(!hashed){
        StringBuilder s = new StringBuilder(blockID+":"+threadID);
        hash = s.toString().hashCode();
        hashed= true;
    }

    return hash;
}

Millions of these objects are created during runtime, so I thought by making the following change, it would provide some speedup:

Version 2

@Override
public int hashCode(){
    if(!hashed){
        StringBuilder s = new StringBuilder(blockID);
        s.append(":");
        s.append(threadID);
        hash = s.toString().hashCode();
        hashed = true;
    }

    return hash;
}

WRONG! Turns out, Version 2 is literally 100X slower than Version 1. Why???

Additional Info

I am compiling against Java 6 (customer requirement), and I am using Oracle's JVM.

My performance test involves creating a million of these objects and throwing them into a HashMap. It takes half a second to do it using Version 1, but almost 50 seconds to do it using Version 2.

like image 957
CodeBlind Avatar asked Dec 06 '22 23:12

CodeBlind


2 Answers

Because you're inadvertently setting the initial capacity of the StringBuilder instead of appending blockID to it. See constructor documentation here.

public StringBuilder(int capacity)

Constructs a string builder with no characters in it and an initial capacity specified by the capacity argument.

Try this instead:

StringBuilder s = new StringBuilder(9);
s.append(blockID).append(':').append(threadID);
like image 64
gknicker Avatar answered Jan 22 '23 23:01

gknicker


You need to check your test as your first case is actually doing.

public int hashCode(){
    if(!hashed){
        StringBuilder s = new StringBuilder(
                  new StringBuilder(blockID).append(":").append(threadID).toString());
        hash = s.toString().hashCode();
        hashed= true;
    }

    return hash;
}

In other words it is doing everything in the second case and more so it will be slower.

In short, I suspect your test is wrong, not that you are getting better performance.

like image 24
Peter Lawrey Avatar answered Jan 22 '23 21:01

Peter Lawrey