java – JMH puzzle:StringBuilder vs StringBand

It's hard for me to understand the progress of this benchmark I want to measure how my sample class stringband works compared to StringBuilder The idea of stringband is to concatenate strings on toString () instead of append ()

source

This is the stringband source – split benchmark:

public class StringBandSimple {

private String[] array;
private int index;
private int length;

public StringBandSimple(int initialCapacity) {
    array = new String[initialCapacity];
}

public StringBandSimple append(String s) {
    if (s == null) {
        s = StringPool.NULL;
    }
    if (index >= array.length) {
        //expandCapacity();
    }
    array[index++] = s;
    length += s.length();
    return this;
}

public String toString() {
    if (index == 0) {
        return StringPool.EMPTY;
    }

    char[] destination = new char[length];
    int start = 0;
    for (int i = 0; i < index; i++) {
        String s = array[i];
        int len = s.length();
        //char[] chars = UnsafeUtil.getChars(s);
        //System.arraycopy(chars,destination,start,len);
        s.getChars(0,len,start);
        start += len;
    }
    return new String(destination);
}
}

This code uses: unsafeutil Getchars() actually gets string char [] without copying, see code here We can also use getchars (), which is still the same

This is the jmh test:

@State
public class StringBandBenchmark {

String string1;
String string2;

@Setup
public void prepare() {
    int len = 20;
    string1 = RandomStringUtil.randomAlphaNumeric(len);
    string2 = RandomStringUtil.randomAlphaNumeric(len);
}

@GenerateMicroBenchmark
public String stringBuilder2() {
    return new StringBuilder(string1).append(string2).toString();
}

@GenerateMicroBenchmark
public String stringBand2() {
    return new StringBandSimple(2).append(string1).append(string2).toString();
}

}

analysis

This is my understanding of what happens when two strings of 20 characters are added

StringBuilder

>Create new characters [20 16] (36 characters) > call arraycopy to copy 20 string1 characters to StringBuilder > before the second attachment, StringBuilder expands the capacity because 40 > 36 > therefore, create a new char [36 * 2 2] > copy the array of 20 characters to the arraycopy of new buffer > 20 characters to attach sencond string2 > finally, Tostring() returns a new string (buffer, 40)

StringBand

>Create a new string [2] > both just keep the string in the internal buffer until tostring() > the length is increased twice > create a new char [40] (the total length of the result string) > arraycopy of 20 first string characters (unsafeutil provides the real char [] buffer of the character string) > arraycopy of 20 second string characters > finally, Returns a new string (buffer, 40)

expect

Using stringband, we have:

>One less arraycopy – what is the purpose of this > less allocated size: new string [] and new char [] and two new char [] > plus we don't have as many checks as the StringBuilder method (for size, etc.)

So I hope stringband is at least the same as StringBuilder, if not faster

Benchmark test results

I ran benchmarks on MacBook Pro in mid - 2013 Use JMH v0 2 and Java 1.7b45

Command:

java -jar build/libs/microbenchmarks.jar .*StringBand.* -wi 2 -i 10 -f 2 -t 2

The number of warm - up iterations (2) is good because I can see that the second iteration achieves the same performance

Benchmark                                    Mode Thr     Count  Sec         Mean   Mean error    Units
j.b.s.StringBandBenchmark.stringBand2       thrpt   2        20    1    37806.993      174.637   ops/ms
j.b.s.StringBandBenchmark.stringBuilder2    thrpt   2        20    1    76507.744      582.131   ops/ms

The results show that the speed of StringBuilder is doubled The same happens when I increase the number of threads to 16 or explicitly use Blackholes in my code

Why?

Solution

Well, as usual, "owls are not what they look like." It's strange to reason about code performance by checking java code quickly It feels the same to reason by looking at the bytecode The generated code disassembly should know more about this, even in some small cases, the assembly is too high to explain this phenomenon

This is because the platform has greatly optimized the code at all levels This is a hint you should read At i5 2.0 GHz, Linux x86_ 64, run your benchmark on JDK 7u40

Baseline:

Benchmark                                    Mode Thr     Count  Sec         Mean   Mean error    Units
j.b.s.StringBandBenchmark.stringBand2       thrpt   2        20    1    25800.465      297.737   ops/ms
j.b.s.StringBandBenchmark.stringBuilder2    thrpt   2        20    1    55552.936      876.021   ops/ms

Yes, it's amazing Now, look at this There's nothing in my sleeve except

-XX:-OptimizeStringConcat:

Benchmark                                    Mode Thr     Count  Sec         Mean   Mean error    Units
j.b.s.StringBandBenchmark.stringBand2       thrpt   2        20    1    25727.363      207.979   ops/ms
j.b.s.StringBandBenchmark.stringBuilder2    thrpt   2        20    1    17233.953      219.510   ops/ms

Disabling VM string optimization will produce "expected" results, as described in the original analysis As we all know, hotspot has the optimization function of stringbuilders and can effectively identify new stringbuilders () append(…). append(…). Tostring() and other common idioms, and generate more effective code for statements

Disassemble and find out what happened to the applied string optimization, and leave it to the interested readers to practice:)

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>