字符串初始化和性能

时间:2014-08-30 10:05:22

标签: java performance methods

这是一个与绩效相关的问题:

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 );        
    }     
}

4 个答案:

答案 0 :(得分:3)

查看此question,其中介绍了如何设置好的微基准测试。

两个要点是:

  • 进行一个热身阶段,你可以用你的方法打电话几万次 这样JIT将缓存该方法。
  • 如果您在repeatPrint2()之前调用repeatPrint1(),反之亦然

您在代码中获得的结果在任何运行中都会有所不同。花一些时间来设置一个好的基准来获得有价值的结果。

答案 1 :(得分:3)

使用javap读取字节代码,并比较两种方法转换。

从你发布的代码中,我认为它几乎是一样的:编译器应该将局部变量(如repeatPrint2)移动到方法的顶部。

您遇到的可能是JIT编译器启动并动态优化您的代码。

我个人更喜欢在使用它的范围内而不是在外部范围内声明变量(例如:像repeatPrint2):这对于可读性和重构更好。

答案 2 :(得分:1)

您提供的两种方法基本相同。你的时间被抛弃主要是因为JVM没有升温;在方法或循环重复超过10,000次之后,热点才会启动。经过多次重复之后,您通常也可以使用GC进行特殊操作,以及混合中的操作系统进程。

要在基准测试中解决其中一些问题,请将每种方法预热10k次并重复几次。对于操作系统噪音,请务必关闭尽可能多的其他应用程序和进程,并确保计算机处于空闲状态。操作系统当然也可以进行调整,但暂时不要将其作为主题。下面是一些可以给你一个想法的示例代码,我还添加了两种优化循环的方法;只是为了好玩;)

我包括了我到底的时间。需要注意的事项是

  1. 你所包含的两种方法开始变化,但正常化为快速相同
  2. 通过调用System.out,您的代码成本相形见绌;换句话说就是io本身。为了证明我已经给出了另外两种选择,一种是对System.out进行更多调用,另一种是对System.out进行更少调用。
  3. 估计时间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)

主要原因是性能是一个随机变量,因此,如果您有可靠的数据要详细说明,您可能首先要生成合理数量的数据点,以便您可以执行统计分析。