【发布时间】:2013-02-17 03:35:42
【问题描述】:
我目前正在研究字符串连接选项以及它们对整体性能的影响。我的测试用例产生的结果让我大吃一惊,我不确定我是否忽略了某些东西。
这里是交易:在 java 中执行 "something"+"somethingElse" 将(在编译时)在每次完成时创建一个新的 StringBuilder。
对于我的测试用例,我正在从我的 HDD 加载一个文件,该文件包含 1661 行示例数据(经典的“Lorem Ipsum”)。这个问题不是关于 I/O 性能,而是关于不同字符串 concat 方法的性能。
public class InefficientStringConcat {
public static void main(String[] agrs) throws Exception{
// Get a file with example data:
System.out.println("Starting benchmark");
// Read an measure:
for (int i = 0; i < 10; i++){
BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream(new File("data.txt")))
);
long start = System.currentTimeMillis();
// Un-comment method to test:
//inefficientRead(in);
//betterRead(in);
long end = System.currentTimeMillis();
System.out.println("Took "+(end-start)+"ms");
in.close();
}
}
public static String betterRead(BufferedReader in) throws IOException{
StringBuilder b = new StringBuilder();
String line;
while ((line = in.readLine()) != null){
b.append(line);
}
return b.toString();
}
public static String inefficientRead(BufferedReader in) throws IOException {
String everything = "", line;
while ((line = in.readLine()) != null){
everything += line;
}
return everything;
}
}
如您所见,两个测试的设置相同。结果如下:
使用inefficientRead()-方法:
Starting benchmark
#1 Took 658ms
#2 Took 590ms
#3 Took 569ms
#4 Took 567ms
#5 Took 562ms
#6 Took 570ms
#7 Took 563ms
#8 Took 568ms
#9 Took 560ms
#10 Took 568ms
使用betterRead()-方法
Starting benchmark
#1 Took 42ms
#2 Took 10ms
#3 Took 5ms
#4 Took 7ms
#5 Took 16ms
#6 Took 3ms
#7 Took 4ms
#8 Took 5ms
#9 Took 5ms
#10 Took 13ms
我正在运行java-command 没有额外参数的测试。我正在运行MacMini3,1 from early 2009 和 Sun JDK 7:
[luke@BlackBox ~]$ java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) Client VM (build 23.5-b02, mixed mode)
这让我觉得差别很大。我在衡量这个时做错了什么,还是应该发生这种情况?
【问题讨论】:
-
嗯,10 次迭代不足以作为基准测试,但除此之外它似乎 很好。尝试将迭代次数增加到 100,000 次,并添加一个“热身”阶段,在该阶段调用该方法 10,000 次(不计时),让 JIT 进行一些内联。
-
@CameronSkinner 显然您没有考虑到每个高级迭代已经包含 1661 次方法调用。
-
@MarkoTopolnik:基准测试正在通过
betterRead或inefficientRead方法进行测量,因此您应该多次调用这些方法。所以你是对的:你没有考虑被测方法的实现细节。 -
@CameronSkinner 至少调用这些方法 10,000 次只是为了热身的建议显然是错误的:JVM JIT 根据通过任何单个的次数代码行,这里发生在第十次外部迭代之前。从那开始,您要求执行 100,000 次完整的文件加载过程是非常不合理的。即使是最严格的统计学家,最大 100 的样本量也能满足。
-
@MarkoTopolnik:我们可以同意不同意。但我建议您详细了解 JIT 在内联整个方法调用等方面可以做什么。
标签: java performance-testing string-concatenation