StringBuilder vs. .concat vs.“+”操作员相对性能在eclipse中不同于命令行? [英] StringBuilder vs. .concat vs. "+" Operator relative performance different in eclipse than command line?
问题描述
我也读过.concat字符串上的方法是最糟糕的所有时间的选择(这么多,以致于它成为一个错误 Findbugs !)。
所以我决定自己测试自己在Eclipse中写一个java类。我的结果让我有点惊讶我发现,不同的方法比较快或慢,如果我遵守并在命令行中运行它们。
首先我的日食结果是: p>
总计毫秒与+连接是:12154
与.concat连接的总毫秒数为:8840
总共与StringBuilder连接的是:11350
与指定大小的StringBuilder连接的总毫秒数为:5611
所以在eclipse的StringBuilder中,指定的大小是最快的,然后是.concat(很奇怪),那么StringBuilder和+连接几乎相同。
但是,我的命令行结果是:
与+连接的总毫秒数为:4139
总共毫秒连接到.concat是:8590
总共毫秒连接到StringBuilder是:10888
总的毫秒连接与StringBuilder机智ha指定的大小是:6033
所以当我从commnad行编译并运行+运算符显然是最快的,其次是String builder的大小,然后是concat,最后是正常的StringBuilder!
这对我来说没有意义。显然,所有的stackoverflow答案我读到说,+运算符编译成正常的旧StringBuilder实例必须是过时的。有没有人知道这里真的发生了什么?
我使用的是jdk1.7.0_07,所以我可以告诉eclipse和我的命令行引用完全相同的命令行。我知道的唯一的区别是eclipse是使用javaw,但是从我读过的,这不应该有所作为。
这是我的测试类if你想验证我没有做错什么,但我很确定它是坚实的。
public class Test {
static final int LOOPS = 100000000;
static final String FIRST_STRING =这是这样的;
static final String SECOND_STRING =an awesomely cool;
static final String THIRD_STRING =写入字符串。
/ **
* @param args
* /
public static void main(String [] args){
测试。 plusOperator();
Test.dotConcat();
Test.stringBuilder();
Test.stringBuilderSizeSpecified();
}
public static void plusOperator(){
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
日历startTime = Calendar.getInstance(); (int x = 0; x {
String toPrint = localOne + localTwo + localThree;
}
日历endTime = Calendar.getInstance();
System.out.println(总计毫秒连接+是:+
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
public static void stringBuilder(){
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
日历startTime = Calendar.getInstance(); (int x = 0; x StringBuilder toBuild = new StringBuilder()
.append(localOne)
.append(localTwo)
.append(localThree);
}
日历endTime = Calendar.getInstance();
System.out.println(与StringBuilder连接的总毫秒数是:+
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
public static void stringBuilderSizeSpecified(){
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
日历startTime = Calendar.getInstance(); (int x = 0; x< LOOPS; x ++){
StringBuilder toBuild = new StringBuilder(50)
.append(localOne)
.append(localTwo)
.append(localThree);
}
日历endTime = Calendar.getInstance();
System.out.println(与指定大小的StringBuilder连接的总毫秒数为:+
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
public static void dotConcat(){
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
日历startTime = Calendar.getInstance(); (int x = 0; x String toPrint = localOne.concat(localTwo).concat(localThree);
}
日历endTime = Calendar.getInstance();
System.out.println(与.concat连接的总毫秒数为:+
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
}
在Oracle JDK 1.7(javac 1.7.0_17)中,+运算符仍然使用 StringBuilder
实现,如运行 javap- c
在类中获取字节码(仅显示此循环):
public static void plusOperator();
代码:
16:iload 4
18:ldc#10 // int 100000000
20:if_icmpge 53
23:new#11 // class java / lang / StringBuilder
26:dup
27:invokespecial#12 //方法java / lang / StringBuilder< init>:()V
30:aload_0
31:invokevirtual#13 //方法java / lang / StringBuilder.append:(Ljava / lang / String;)Ljava / lang / StringBuilder;
34:aload_1
35:invokevirtual#13 //方法java / lang / StringBuilder.append :( Ljava / lang / String;)Ljava / lang / StringBuilder;
38:aload_2
39:invokevirtual#13 //方法java / lang / StringBuilder.append :( Ljava / lang / String;)Ljava / lang / StringBuilder;
42:invokevirtual#14 //方法java / lang / StringBuilder.toString :()Ljava / lang / String;
45:astore 5
47:iinc 4,1
50:goto 16
public static void stringBuilder();
代码:
16:iload 4
18:ldc#10 // int 100000000
20:if_icmpge 50
23:new#11 // class java / lang / StringBuilder
26:dup
27:invokespecial#12 //方法java / lang / StringBuilder< init>:()V
30:aload_0
31:invokevirtual#13 //方法java / lang / StringBuilder.append:(Ljava / lang / String;)Ljava / lang / StringBuilder;
34:aload_1
35:invokevirtual#13 //方法java / lang / StringBuilder.append :( Ljava / lang / String;)Ljava / lang / StringBuilder;
38:aload_2
39:invokevirtual#13 //方法java / lang / StringBuilder.append :( Ljava / lang / String;)Ljava / lang / StringBuilder;
42:astore 5
44:iinc 4,1
47:goto 16
这两者之间的唯一区别是,带有+的版本将 StringBuilder
转换为 String
在循环中。
所以问题变成:为什么你的测试显示相同代码的这样不同的结果。或者更完整地,为什么这不是有效的微基准测试。以下是一些可能的原因:
- 您正在计算停电时间。这意味着您实际上是在运行测试时测量JVM所做的一切。其中包括垃圾回收(这很重要,因为您正在创建大量垃圾)。您可以通过获取线程CPU时间来减轻这种情况。
- 您不会验证HotSpot是否在编译方法。这就是为什么你应该在任何微型基准测试之前做一个预热阶段:基本上,在运行实际测试之前,多次运行你的
main()
。
I was reading about how when possible the java compiler will compile strings concatenated with the "+" operator into instances of StringBuilder, and how this makes it better to use the simple "+" operator since they compile to the same code. (Except when you are building the string in a while loop, in which case it is clearly best to use a StringBuilder.)
I've also read that the .concat method on strings is the worst choice all the time (so much so that it was made into a bug by Findbugs!).
So I decided to test it myself writing a little java class in eclipse. My results surprised me a bit. What I found was that different methods were relatively faster or slower if I complied and ran them in eclipse vs. on the command line.
First my eclipse results were:
the total millis to concatenate with + was: 12154
the total millis to concatenate with .concat was: 8840
the total millis to concatenate with StringBuilder was: 11350
the total millis to concatenate with StringBuilder with a specified size was: 5611
So in eclipse StringBuilder with the size specified was fastest, followed by .concat (weird), then StringBuilder and "+" concatenation were pretty much the same.
My results on the command line, however, were:
the total millis to concatenate with + was: 4139
the total millis to concatenate with .concat was: 8590
the total millis to concatenate with StringBuilder was: 10888
the total millis to concatenate with StringBuilder with a specified size was: 6033
So when I compiled and ran from the commnad line the "+" operator was clearly the fastest, followed by String builder with size, then concat, and last was normal StringBuilder!
This doesn't make sense to me. Obviously all the stackoverflow answers I read saying that + operators compile into normal old StringBuilder instances must be outdated.
Does anyone know what's really going on here?
I'm using jdk1.7.0_07, and so far as I can tell both eclipse and my command line are referencing the exact same one. The only difference I know of is eclipse is using "javaw", but from what I've read, that shouldn't make a difference.
Here's my test class if you want to verify I'm not doing anything wrong, but I'm pretty sure it's solid.
public class Test {
static final int LOOPS = 100000000;
static final String FIRST_STRING = "This is such";
static final String SECOND_STRING = " an awesomely cool ";
static final String THIRD_STRING = "to write string.";
/**
* @param args
*/
public static void main(String[] args) {
Test.plusOperator();
Test.dotConcat();
Test.stringBuilder();
Test.stringBuilderSizeSpecified();
}
public static void plusOperator() {
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
Calendar startTime = Calendar.getInstance();
for (int x = 0; x < LOOPS; x++) {
String toPrint = localOne + localTwo + localThree;
}
Calendar endTime = Calendar.getInstance();
System.out.println("the total millis to concatenate with + was: " +
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
public static void stringBuilder() {
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
Calendar startTime = Calendar.getInstance();
for (int x = 0; x < LOOPS; x++) {
StringBuilder toBuild = new StringBuilder()
.append(localOne)
.append(localTwo)
.append(localThree);
}
Calendar endTime = Calendar.getInstance();
System.out.println("the total millis to concatenate with StringBuilder was: " +
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
public static void stringBuilderSizeSpecified() {
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
Calendar startTime = Calendar.getInstance();
for (int x = 0; x < LOOPS; x++) {
StringBuilder toBuild = new StringBuilder(50)
.append(localOne)
.append(localTwo)
.append(localThree);
}
Calendar endTime = Calendar.getInstance();
System.out.println("the total millis to concatenate with StringBuilder with a specified size was: " +
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
public static void dotConcat() {
String localOne = FIRST_STRING;
String localTwo = SECOND_STRING;
String localThree = THIRD_STRING;
Calendar startTime = Calendar.getInstance();
for (int x = 0; x < LOOPS; x++) {
String toPrint = localOne.concat(localTwo).concat(localThree);
}
Calendar endTime = Calendar.getInstance();
System.out.println("the total millis to concatenate with .concat was: " +
(endTime.getTimeInMillis() - startTime.getTimeInMillis()));
}
}
On Oracle JDK 1.7 (javac 1.7.0_17), the "+" operator is still implemented using StringBuilder
, as shown by running javap -c
on the class to get the bytecode (only showing the loops here):
public static void plusOperator();
Code:
16: iload 4
18: ldc #10 // int 100000000
20: if_icmpge 53
23: new #11 // class java/lang/StringBuilder
26: dup
27: invokespecial #12 // Method java/lang/StringBuilder."<init>":()V
30: aload_0
31: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: aload_1
35: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
38: aload_2
39: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
42: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
45: astore 5
47: iinc 4, 1
50: goto 16
public static void stringBuilder();
Code:
16: iload 4
18: ldc #10 // int 100000000
20: if_icmpge 50
23: new #11 // class java/lang/StringBuilder
26: dup
27: invokespecial #12 // Method java/lang/StringBuilder."<init>":()V
30: aload_0
31: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: aload_1
35: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
38: aload_2
39: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
42: astore 5
44: iinc 4, 1
47: goto 16
The only difference between these two is that the version with "+" converts the StringBuilder
to a String
within the loop.
So the question becomes: why does your test show such different results for the same code. Or more completely, why is this not a valid micro-benchmark. Here are some possible reasons:
- You're counting wall-clock time. This means that you're actually measuring everything that the JVM is doing while running your test. Which includes garbage collection (which is important because you're creating a lot of garbage). You can mitigate this by getting the thread CPU time.
- You don't verify when or if HotSpot is compiling the methods. This is why you should do a warmup phase before any micro-benchmarks: basically, run your
main()
multiple times, before you run your actual test.
这篇关于StringBuilder vs. .concat vs.“+”操作员相对性能在eclipse中不同于命令行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!