Java循环优化

And*_*anu 7 java optimization performance

提供以下(直接)代码:

public class pr1 {

    public static void f1(){
        long sx = 0, s;
        s = System.currentTimeMillis();
        for(long i = 0; i < Integer.MAX_VALUE; ++i){
            sx += i;
        }
        System.out.println("f1(): " + (System.currentTimeMillis() - s));
    }

    public static void f2(){
        long sx = 0, s, i;
        s = System.currentTimeMillis();
        i = Integer.MAX_VALUE;
        while(i-->0){
            sx+=i;
        }
        sx += Integer.MAX_VALUE;
        System.out.println("f2(): " + (System.currentTimeMillis() - s));
    }

    public static void f3(){
        long sx = 0, s, i;
        s = System.currentTimeMillis();
        i = Integer.MAX_VALUE;
        while(--i>0){
            sx+=i;
        }
        sx += Integer.MAX_VALUE;
        System.out.println("f3(): " + (System.currentTimeMillis() - s));
    }

    public static void f4(){
        long sx = 0, s, i;
        s = System.currentTimeMillis();
        i = Integer.MAX_VALUE;
        do{
            sx+=i;
        }while(--i>0);
        System.out.println("f4(): " + (System.currentTimeMillis() - s));
    }

    public static void main(String args[]){
        f1();
        f2();
        f3();
        f4();
    }
}
Run Code Online (Sandbox Code Playgroud)

以及运行代码后的实际结果:

f1(): 5828
f2(): 8125
f3(): 3406
f4(): 3781
Run Code Online (Sandbox Code Playgroud)

能否请您解释一下时间的巨大差异?理论上,循环实现了相同的功能,但在实践中,似乎四个版本中的每一个都存在相关的时间差异.

重复执行后,结果非常相似.

后期编辑 作为另一项测试我改写了主要方法:

public static void main(String args[]){
    for(int i = 0; i < 4; ++i){
        f1(); f2(); f3(); f4();
    }
}
Run Code Online (Sandbox Code Playgroud)

新的结果是:

f1(): 5906
f2(): 8266
f3(): 3406
f4(): 3844
f1(): 5843
f2(): 8125
f3(): 3438
f4(): 3859
f1(): 5891
f2(): 8156
f3(): 3406
f4(): 3813
f1(): 5859
f2(): 8172
f3(): 3438
f4(): 3828
Run Code Online (Sandbox Code Playgroud)

10次​​重复:

f1(): 5844
f2(): 8156
f3(): 3453
f4(): 3813
f1(): 5844
f2(): 8218
f3(): 3485
f4(): 3937
f1(): 5985
f2(): 8156
f3(): 3422
f4(): 3781
f1(): 5828
f2(): 8234
f3(): 3469
f4(): 3828
f1(): 5844
f2(): 8328
f3(): 3422
f4(): 3859
f1(): 5844
f2(): 8188
f3(): 3406
f4(): 3797
f1(): 5906
f2(): 8219
f3(): 3422
f4(): 3797
f1(): 5843
f2(): 8203
f3(): 3454
f4(): 3906
f1(): 5844
f2(): 8140
f3(): 3469
f4(): 3812
f1(): 5860
f2(): 8109
f3(): 3422
f4(): 3813
Run Code Online (Sandbox Code Playgroud)

在去除循环之间的微积分后,结果仍然有点不同:

public class pr2 {

    public static void f1(){
        long sx = 0, s;
        s = System.currentTimeMillis();
        for(long i = 0; i < Integer.MAX_VALUE; ++i);
        System.out.println("f1(): " + (System.currentTimeMillis() - s));
    }

    public static void f2(){
        long sx = 0, s, i;
        s = System.currentTimeMillis();
        i = Integer.MAX_VALUE;
        while(i-->0);
        System.out.println("f2(): " + (System.currentTimeMillis() - s));
    }

    public static void f3(){
        long sx = 0, s, i;
        s = System.currentTimeMillis();
        i = Integer.MAX_VALUE;
        while(--i>0);
        System.out.println("f3(): " + (System.currentTimeMillis() - s));
    }

    public static void f4(){
        long sx = 0, s, i;
        s = System.currentTimeMillis();
        i = Integer.MAX_VALUE;
        do{
        }while(--i>0);
        System.out.println("f4(): " + (System.currentTimeMillis() - s));
    }

    public static void main(String args[]){
        for(int i = 0; i < 2; ++i){
            f1(); f2(); f3(); f4();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但时差仍然存在:

f1(): 3219
f2(): 4859
f3(): 2610
f4(): 3031
f1(): 3219
f2(): 4812
f3(): 2610
f4(): 3062
Run Code Online (Sandbox Code Playgroud)

JVM:

java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) Client VM (build 16.3-b01, mixed mode, sharing)
Run Code Online (Sandbox Code Playgroud)

LATER EDIT: 对于第一个版本,我使用了-av参数为javac.新的结果是:

f1(): 3219
f2(): 4859
f3(): 2610
f4(): 3031
Run Code Online (Sandbox Code Playgroud)

稍后编辑

好的,我在家里尝试使用相同的代码,使用Linux机器:

java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8) (6b18-1.8-0ubuntu1)
OpenJDK Server VM (build 14.0-b16, mixed mode)
Run Code Online (Sandbox Code Playgroud)

结果是"正常的".现在没问题:

f1(): 7495
f2(): 7418
f3(): 7457
f4(): 7384
Run Code Online (Sandbox Code Playgroud)

Bal*_*usC 10

您实际上是对JVM进行基准测试,而不是代码.

也可以看看:


更新:好的,这是一个有点简单的回答.使用后缀运算符(i--)的循环似乎比使用前缀运算符(--i)的循环慢.这可能是真的,因为在评估表达式期间更改了值,但编译器需要保存要在表达式中使用的原始值的副本.使用前缀运算符可以避免保留副本,因为只需在表达式中使用更改的值.

也可以看看:

毕竟,这次微观优化会在2 31次执行中为你节省一两秒钟.您是否真的经常执行它?我更喜欢可读性而不是过早优化.


Avi*_*Avi 5

当我在我的JVM(Java HotSpot(TM)64位服务器VM(构建16.0-b13,混合模式))上运行此代码时,所有四个函数都给出类似的结果:

f1(): 3234
f2(): 3132
f3(): 3114
f4(): 3089
Run Code Online (Sandbox Code Playgroud)

我猜你的JVM并没有在某处进行相同的优化.

您可以使用javap检查为不同函数生成的字节码:javap -l -c pr1.当我这样做时,我得到f2()的以下内容:

public static void f2();
  Code:
   0:   lconst_0
   1:   lstore_0
   2:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   5:   lstore_2
   6:   ldc2_w  #3; //long 2147483647l
   9:   lstore  4
   11:  lload   4
   13:  dup2
   14:  lconst_1
   15:  lsub
   16:  lstore  4
   18:  lconst_0
   19:  lcmp
   20:  ifle    31
   23:  lload_0
   24:  lload   4
   26:  ladd
   27:  lstore_0
   28:  goto    11
   31:  lload_0
   32:  ldc2_w  #3; //long 2147483647l
   35:  ladd
   36:  lstore_0
   37:  getstatic       #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   40:  new     #6; //class java/lang/StringBuilder
   43:  dup
   44:  invokespecial   #7; //Method java/lang/StringBuilder."<init>":()V
   47:  ldc     #13; //String f2():
   49:  invokevirtual   #9; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   52:  invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   55:  lload_2
   56:  lsub
   57:  invokevirtual   #10; //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
   60:  invokevirtual   #11; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   63:  invokevirtual   #12; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   66:  return
Run Code Online (Sandbox Code Playgroud)

f2()较慢的一个可能原因可能是编译器/ JVM对while(i-->0)后递减运算符不聪明.基本上,您需要i增量之前和之后的值,因此如果天真地实现,此操作涉及更多工作.