我很难理解此基准测试的情况。与相比,我想衡量我的样本课程的StringBand
工作方式StringBuilder
。的想法StringBand
是将字符串连接到toString()
,而不是append()
。
这是StringBand
来源-删除了基准:
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, 0, destination, start, len);
s.getChars(0, len, destination, start);
start += len;
}
return new String(destination);
}
}
此代码使用:UnsafeUtil.getChars()
实际获取String
char []而不进行复制,请参见此处的代码。我们也可以使用getChars()
和它仍然相同。
这是JMH测试:
@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();
}
}
这是我对添加两个20个字符的字符串时发生的情况的理解。
new char[20+16]
已创建(36个字符)arraycopy
被要求将20个string1
字符复制到StringBuilder
StringBuilder
扩展容量,因为40> 36new char[36*2+2]
被创建arraycopy
20个字符到新缓冲区arraycopy
追加20个字符 string2
toString()
回报new String(buffer, 0, 40)
new String[2]
被建造toString()
被调用length
增加两次new char[40]
已创建(结果字符串的总长度)arraycopy
20个第一个字符串字符(UnsafeUtil
提供字符串的实际char[]
缓冲区)arraycopy
20秒字符串new String(buffer, 0, 40)
随着StringBand
我们有:
arraycopy
-这样做的全部目的是什么new String[]
和new char[]
vs. 2new char[]
StringBuilder
方法,例如尺寸等因此,我希望它的StringBand
运行速度至少与相同StringBuilder
,甚至更快。
我在MacBookPro上运行基准测试,2013年中。使用JMH v0.2和Java 1.7b45
命令:
java -jar build/libs/microbenchmarks.jar .*StringBand.* -wi 2 -i 10 -f 2 -t 2
预热迭代次数(2)很好,因为我可以看到第二次迭代达到了相同的性能。
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
结果说StringBuilder
快了两倍。当我将线程数增加到16,或BlackHole
在代码中显式使用s时,也会发生同样的情况。
为什么?
好吧,像往常一样,“猫头鹰不是它们看起来的样子”。通过快速检查Java代码对代码性能进行推理变得很奇怪。通过查看字节码进行推理的感觉是相同的。即使在极少数情况下汇编太高级而无法解释这种现象,生成的代码反汇编也应对此有所启发。
这是因为平台在每个级别都对代码进行了优化。这是您应该看的提示。在i5 2.0 GHz,Linux x86_64,JDK 7u40上运行基准测试。
基线:
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
是的,令人惊讶。现在,观看此。我的袖子什么都没有,除了...
-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
如原始分析所示,从字符串优化中禁止虚拟机产生“预期”结果。众所周知,HotSpot具有针对StringBuilders的优化,可以有效地识别常见的用法,new StringBuilder().append(...).append(...).toString()
并为语句生成更有效的代码。
拆卸和弄清楚应用字符串优化后到底发生了什么,留给有兴趣的读者练习:)
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句