这是一个与绩效相关的问题:
在repeatPrint1()
方法中,我将一些String
属性初始化为null
,然后在循环中使用它们。
在repeatPrint2()
方法中,我直接在循环内初始化了一些String
属性。
当我进行测试时,结果令人惊讶 - repeatPrint2()
表现优于repeatPrint1()
最多可达2500条记录,但在此之后repeatPrint1()
的效果优于repeatPrint2()
。
任何人都可以解释为什么会这样吗? 在代码审查中,我们可以采取什么样的正确方法?
代码段:
public static void main(String[] args) throws Exception {
long startTime1 = System.nanoTime();
repeatPrint1();
long estimatedTime1 = System.nanoTime() - startTime1;
long startTime2 = System.nanoTime();
repeatPrint2();
long estimatedTime2 = System.nanoTime() - startTime2;
System.out.println("estimatedTime1: " + estimatedTime1);
System.out.println("estimatedTime2: " + estimatedTime2);
}
private static void repeatPrint1() {
String name1 = null;
String name2 = null;
String name3 = null;
String name4 = null;
String name5 = null;
for(int x = 10; x < 2500; x = x + 1) {
name1 = "My Name is AAAA" + x;
name2 = "My Name is BBBB" + x;
name3 = "My Name is CCCC" + x;
name4 = "My Name is DDDD" + x;
name5 = "My Name is EEEE" + x;
System.out.print("name1 : " + name1);
System.out.print("name2 : " + name2);
System.out.print("name3 : " + name3);
System.out.print("name4 : " + name4);
System.out.print("name5 : " + name5);
System.out.println("value of x : " + x);
}
}
private static void repeatPrint2() {
for(int x = 10; x < 2500; x = x + 1) {
String Sname1 = "My Name is AAAA" + x;
String Sname2 = "My Name is BBBB" + x;
String Sname3 = "My Name is CCCC" + x;
String Sname4 = "My Name is DDDD" + x;
String Sname5 = "My Name is EEEE" + x;
System.out.print("Sname1 : " + Sname1 );
System.out.print("Sname2 : " + Sname2 );
System.out.print("Sname3 : " + Sname3 );
System.out.print("Sname4 : " + Sname4 );
System.out.print("Sname5 : " + Sname5 );
System.out.println("value of x : " + x );
}
}
答案 0 :(得分:3)
查看此question,其中介绍了如何设置好的微基准测试。
两个要点是:
您在代码中获得的结果在任何运行中都会有所不同。花一些时间来设置一个好的基准来获得有价值的结果。
答案 1 :(得分:3)
使用javap
读取字节代码,并比较两种方法转换。
从你发布的代码中,我认为它几乎是一样的:编译器应该将局部变量(如repeatPrint2
)移动到方法的顶部。
您遇到的可能是JIT编译器启动并动态优化您的代码。
我个人更喜欢在使用它的范围内而不是在外部范围内声明变量(例如:像repeatPrint2
):这对于可读性和重构更好。
答案 2 :(得分:1)
您提供的两种方法基本相同。你的时间被抛弃主要是因为JVM没有升温;在方法或循环重复超过10,000次之后,热点才会启动。经过多次重复之后,您通常也可以使用GC进行特殊操作,以及混合中的操作系统进程。
要在基准测试中解决其中一些问题,请将每种方法预热10k次并重复几次。对于操作系统噪音,请务必关闭尽可能多的其他应用程序和进程,并确保计算机处于空闲状态。操作系统当然也可以进行调整,但暂时不要将其作为主题。下面是一些可以给你一个想法的示例代码,我还添加了两种优化循环的方法;只是为了好玩;)
我包括了我到底的时间。需要注意的事项是
估计时间1:17.27938毫秒
估计时间2:17.45852毫秒
估计时间3:100.61994毫秒
估计时间4:9.553329999999999 ms
public class Foo {
public static void main( String[] args ) throws Exception {
time1();
time2();
time3();
time4();
time1();
time2();
time3();
time4();
time1();
time2();
time3();
time4();
}
private static final int REPEAT = 100;
private static void time1() {
long startTime1 = System.nanoTime();
for ( int i=0; i<REPEAT; i++ ) {
repeatPrint1();
}
long estimatedTime1 = System.nanoTime() - startTime1;
double dur = estimatedTime1/1000000.0/REPEAT;
System.out.println( "estimatedTime1: " + dur );
}
private static void time2() {
long startTime1 = System.nanoTime();
for ( int i=0; i<REPEAT; i++ ) {
repeatPrint2();
}
long estimatedTime1 = System.nanoTime() - startTime1;
double dur = estimatedTime1/1000000.0/REPEAT;
System.out.println( "estimatedTime2: " + dur );
}
private static void time3() {
long startTime1 = System.nanoTime();
for ( int i=0; i<REPEAT; i++ ) {
repeatPrint3();
}
long estimatedTime1 = System.nanoTime() - startTime1;
double dur = estimatedTime1/1000000.0/REPEAT;
System.out.println( "estimatedTime3: " + dur );
}
private static void time4() {
long startTime1 = System.nanoTime();
for ( int i=0; i<REPEAT; i++ ) {
repeatPrint4();
}
long estimatedTime1 = System.nanoTime() - startTime1;
double dur = estimatedTime1/1000000.0/REPEAT;
System.out.println( "estimatedTime4: " + dur );
}
private static void repeatPrint1() {
String name1 = null;
String name2 = null;
String name3 = null;
String name4 = null;
String name5 = null;
for ( int x = 10; x < 2500; x = x + 1 ) {
name1 = "My Name is AAAA" + x;
name2 = "My Name is BBBB" + x;
name3 = "My Name is CCCC" + x;
name4 = "My Name is DDDD" + x;
name5 = "My Name is EEEE" + x;
System.out.print( "name1 : " + name1 );
System.out.print( "name2 : " + name2 );
System.out.print( "name3 : " + name3 );
System.out.print( "name4 : " + name4 );
System.out.print( "name5 : " + name5 );
System.out.println( "value of x : " + x );
}
}
private static void repeatPrint2() {
for ( int x = 10; x < 2500; x = x + 1 ) {
String Sname1 = "My Name is AAAA" + x;
String Sname2 = "My Name is BBBB" + x;
String Sname3 = "My Name is CCCC" + x;
String Sname4 = "My Name is DDDD" + x;
String Sname5 = "My Name is EEEE" + x;
System.out.print( "Sname1 : " + Sname1 );
System.out.print( "Sname2 : " + Sname2 );
System.out.print( "Sname3 : " + Sname3 );
System.out.print( "Sname4 : " + Sname4 );
System.out.print( "Sname5 : " + Sname5 );
System.out.println( "value of x : " + x );
}
}
private static void repeatPrint3() {
for ( int x = 10; x < 2500; x = x + 1 ) {
pr1( 1, x, "AAAA" );
pr1( 2, x, "BBBB" );
pr1( 3, x, "CCCC" );
pr1( 4, x, "DDDD" );
pr1( 5, x, "EEEE" );
xr1( x );
}
}
private static void pr1( int i, int x, String r ) {
System.out.print( "Sname" );
System.out.print( '0'+i );
System.out.print( " : " );
System.out.print( "My Name is " );
System.out.print( r );
System.out.println( Integer.toString(x) );
}
private static void xr1( int x ) {
System.out.print( "value of x : " );
System.out.println( Integer.toString(x) );
}
private static void repeatPrint4() {
StringBuilder buf = new StringBuilder( 400 );
for ( int x = 10; x < 2500; x = x + 1 ) {
pr2( buf, 1, x, "AAAA" );
pr2( buf, 2, x, "BBBB" );
pr2( buf, 3, x, "CCCC" );
pr2( buf, 4, x, "DDDD" );
pr2( buf, 5, x, "EEEE" );
xr2( buf, x );
System.out.print( buf.toString() );
buf.setLength( 0 );
}
}
private static void pr2( StringBuilder buf, int i, int x, String r ) {
buf.append( "Sname" );
buf.append( '0' + i );
buf.append( " : " );
buf.append( "My Name is " );
buf.append( r );
buf.append( Integer.toString( x ) );
buf.append( "\n" );
}
private static void xr2( StringBuilder buf, int x ) {
buf.append( "value of x : " );
buf.append( Integer.toString( x ) );
buf.append( "\n" );
}
}
答案 3 :(得分:0)
主要原因是性能是一个随机变量,因此,如果您有可靠的数据要详细说明,您可能首先要生成合理数量的数据点,以便您可以执行统计分析。