Java 单行字符串连接的速度差
因此,我一直认为使用“+”操作符在一行上附加字符串与使用StringBuilder一样有效(而且从外观上看肯定更好)。今天,虽然我遇到了一些附加变量和字符串的记录器的速度问题,但它使用了“+”操作符。所以我做了一个快速的测试,让我惊讶的是发现使用StringBuilder更快 我使用了4种不同的方法(如下所示),每个附录的平均运行次数为20次 结果,时间(以毫秒为单位) 我现在已经尝试了浮点、整数和字符串。所有这些都或多或少地显示出相同的时差 问题Java 单行字符串连接的速度差,java,string,profiling,append,timing,Java,String,Profiling,Append,Timing,因此,我一直认为使用“+”操作符在一行上附加字符串与使用StringBuilder一样有效(而且从外观上看肯定更好)。今天,虽然我遇到了一些附加变量和字符串的记录器的速度问题,但它使用了“+”操作符。所以我做了一个快速的测试,让我惊讶的是发现使用StringBuilder更快 我使用了4种不同的方法(如下所示),每个附录的平均运行次数为20次 结果,时间(以毫秒为单位) 我现在已经尝试了浮点、整数和字符串。所有这些都或多或少地显示出相同的时差 问题 “+”运算符显然不是相同的字节码,而且时间与最
Java语言规范没有指定如何执行字符串连接,但我怀疑您的编译器除了执行以下等效操作外,是否还执行其他操作:
new StringBuilder("[").
append(a).
append(",").
append(b).
append(",").
append(c).
append("][").
append(x).
append(",").
append(y).
append(",").
append(z).
append("]").
toString();
您可以使用“javap-c…”来反编译类文件并验证这一点
如果度量方法之间在运行时的任何显著的重复性差异,我更愿意假设垃圾收集器在不同的时间运行,而不是存在任何实际的、显著的性能差异。创建具有不同初始容量的
StringBuilder
s当然可能会有一些影响,但与格式化浮动等所需的工作相比,这应该是微不足道的。我不喜欢您的测试用例有两个方面。首先,在同一进程中运行所有测试。当处理“大”(我知道不明确)时,但当处理进程如何与内存交互是您主要关心的问题时,您应该始终在单独的运行中进行基准测试。事实上,我们已经启动了垃圾收集,这可能会影响早期运行的结果。你计算结果的方式让我有点困惑。我所做的是每一次单独的跑步,并将我的跑步次数减为零。我还让它运行多次“重复”,对每次重复计时,然后打印出每次运行所用的毫秒数。这是我的密码:
import java.util.Random;
public class blah {
public static void main(String[] args){
stringComp();
}
private static void stringComp() {
int SIZE = 1000000;
int NUM_REPS = 5;
for(int j = 0; j < NUM_REPS; j++) {
Random r = new Random();
float f;
long start = System.currentTimeMillis();
for (int i=0;i<SIZE;i++){
f = r.nextFloat();
stringSpeed3(f,f,f,f,f,f);
}
System.out.print((System.currentTimeMillis() - start));
System.out.print(", ");
}
}
public static String stringSpeed1(float a, float b, float c, float x, float y, float z){
StringBuilder sb = new StringBuilder(72).append("[").append(a).append(",").append(b).append(",").append(c).append("][").
append(x).append(",").append(y).append(",").append(z).append("]");
return sb.toString();
}
public static String stringSpeed2(float a, float b, float c, float x, float y, float z){
StringBuilder sb = new StringBuilder().append("[").append(a).append(",").append(b).append(",").append(c).append("][").
append(x).append(",").append(y).append(",").append(z).append("]");
return sb.toString();
}
public static String stringSpeed3(float a, float b, float c, float x, float y, float z){
return "["+a+","+b+","+c+"]["+x+","+y+","+z+"]";
}
public static String stringSpeed4(float a, float b, float c, float x, float y, float z){
return String.format("[%f,%f,%f][%f,%f,%f]", a,b,c,x,y,z);
}
}
从我的结果中可以看出,在“中等范围”的值上,每次连续重复都会加快速度。我相信,这可以通过JVM运行并抓住它所需的内存来解释。随着“大小”的增加,不允许这种影响发生,因为有太多的内存让垃圾收集器无法释放,也让进程无法锁定。此外,当您在执行这样的“重复”基准测试时,当您的大多数进程可以存在于较低级别的缓存中而不是RAM中时,您的进程对分支预测器更加敏感。这些都是非常智能的,可以理解您的进程正在做什么,我想JVM会放大这一点。这也有助于解释为什么初始循环上的值较慢,以及为什么您接近基准测试的方式是一个糟糕的解决方案。这就是为什么我认为你对非“大”值的结果是扭曲的,而且看起来很奇怪。然后,随着基准测试的“内存占用”的增加,这个分支预测的效果(百分比)比您在RAM中添加的大字符串要小
简化结论:你的“大”跑步结果是合理有效的,并且似乎与我的结果相似(尽管我仍然不完全理解你是如何得到结果的,但相比之下,百分比似乎吻合得很好)。但是,由于测试的性质,您对较小运行的结果是无效的。100到….之间的算法行为已修复,由于一些原因,它将其切断,并有研究和数据支持+1运行这些时,您是否在优化?如果没有优化,运算符和函数调用经常会在内存中表现异常,并导致在适当优化时没有的异常性能。因此,我在我的计算机Mac上使用标准默认编译<代码>java版本“1.6.0_45”。我没有使用任何特定的优化标志,我已经反编译了代码,“+”操作符与StringBuilder()不同,尽管我没有尝试使用StringBuilder(“[”);,我来看看,你能把javap的输出添加到你的问题中吗?我在问题中也链接了它。它们都非常相似,但在大容量运行时,少数差异似乎开始引起明显的差异。如果我不是完全错的话,根据字节码,你的stringSpeed5方法被实现为
StringBuilder sb=new StringBuilder(“[”);return sb.toString();
使用字符串连接的等效方法应该是string s=“[”+…;return s;
。如果不使用局部变量,而是直接将第五个方法实现为return new StringBuilder(“[”)…toString();
你应该得到相同的字节码。这看起来很神奇。在“新的StringBuilder([”)”;和直接返回之间,最终使字节码等效。这对我来说意味着两件事。1)另一个问题的答案仍然是错的,因为原始海报分配了一个新的StringBuilder(容量)并返回它,这不会导致等价的字节码。2)我很失望java编译器不够聪明,无法让它分配一个新对象并立即返回它只是返回它……我确信它会自动执行这一操作。我想你对分支预测器有点了解,我认为这是gc倾斜较小的运行,但我真的很喜欢您从运行1,2下降到3…这对我来说意味着数量。对于较大的运行,您显示了相同的效果,方法不等效您假设了许多假设h
new StringBuilder("[").
append(a).
append(",").
append(b).
append(",").
append(c).
append("][").
append(x).
append(",").
append(y).
append(",").
append(z).
append("]").
toString();
import java.util.Random;
public class blah {
public static void main(String[] args){
stringComp();
}
private static void stringComp() {
int SIZE = 1000000;
int NUM_REPS = 5;
for(int j = 0; j < NUM_REPS; j++) {
Random r = new Random();
float f;
long start = System.currentTimeMillis();
for (int i=0;i<SIZE;i++){
f = r.nextFloat();
stringSpeed3(f,f,f,f,f,f);
}
System.out.print((System.currentTimeMillis() - start));
System.out.print(", ");
}
}
public static String stringSpeed1(float a, float b, float c, float x, float y, float z){
StringBuilder sb = new StringBuilder(72).append("[").append(a).append(",").append(b).append(",").append(c).append("][").
append(x).append(",").append(y).append(",").append(z).append("]");
return sb.toString();
}
public static String stringSpeed2(float a, float b, float c, float x, float y, float z){
StringBuilder sb = new StringBuilder().append("[").append(a).append(",").append(b).append(",").append(c).append("][").
append(x).append(",").append(y).append(",").append(z).append("]");
return sb.toString();
}
public static String stringSpeed3(float a, float b, float c, float x, float y, float z){
return "["+a+","+b+","+c+"]["+x+","+y+","+z+"]";
}
public static String stringSpeed4(float a, float b, float c, float x, float y, float z){
return String.format("[%f,%f,%f][%f,%f,%f]", a,b,c,x,y,z);
}
}
stringSpeed1(SIZE = 10000000): 11548, 11305, 11362, 11275, 11279
stringSpeed2(SIZE = 10000000): 12386, 12217, 12242, 12237, 12156
stringSpeed3(SIZE = 10000000): 12313, 12016, 12073, 12127, 12038
stringSpeed1(SIZE = 1000000): 1292, 1164, 1170, 1168, 1172
stringSpeed2(SIZE = 1000000): 1364, 1228, 1230, 1224, 1223
stringSpeed3(SIZE = 1000000): 1370, 1229, 1227, 1229, 1230
stringSpeed1(SIZE = 100000): 246, 115, 115, 116, 113
stringSpeed2(SIZE = 100000): 255, 122, 123, 123, 121
stringSpeed3(SIZE = 100000): 257, 123, 129, 124, 125
stringSpeed1(SIZE = 10000): 113, 25, 14, 13, 13
stringSpeed2(SIZE = 10000): 118, 23, 24, 16, 14
stringSpeed3(SIZE = 10000): 120, 24, 16, 17, 14
//This run SIZE is very interesting.
stringSpeed1(SIZE = 1000): 55, 22, 8, 6, 4
stringSpeed2(SIZE = 1000): 54, 23, 7, 4, 3
stringSpeed3(SIZE = 1000): 58, 23, 7, 4, 4
stringSpeed1(SIZE = 100): 6, 6, 6, 6, 6
stringSpeed2(SIZE = 100): 6, 6, 5, 6, 6
stirngSpeed3(SIZE = 100): 8, 6, 7, 6, 6