Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java循环优化_Java_Performance_Optimization - Fatal编程技术网

Java循环优化

Java循环优化,java,performance,optimization,Java,Performance,Optimization,给出以下(直接)代码: 你能给我解释一下大的时差吗?理论上,循环实现了相同的功能,但实际上,四个版本中的每一个似乎都有相应的时间差 重复执行后,结果几乎相同 以后编辑 作为另一个测试,我重写了主要方法: public static void main(String args[]){ for(int i = 0; i < 4; ++i){ f1(); f2(); f3(); f4(); } } 对于10次重复: f1(): 5844 f2(): 8156 f

给出以下(直接)代码:

你能给我解释一下大的时差吗?理论上,循环实现了相同的功能,但实际上,四个版本中的每一个似乎都有相应的时间差

重复执行后,结果几乎相同

以后编辑 作为另一个测试,我重写了主要方法:

public static void main(String args[]){
    for(int i = 0; i < 4; ++i){
        f1(); f2(); f3(); f4();
    }
}
对于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
除去循环之间的演算后,结果仍然有点不同:

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();
        }
    }
}
JVM:

以后编辑: 对于第一个版本,我为javac使用了-O参数。新的结果是:

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
f1(): 3219
f2(): 4859
f3(): 2610
f4(): 3031
以后编辑

好的,我在家里也试过同样的代码,使用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)
结果是“正常的”。现在没有问题了:

f1(): 7495
f2(): 7418
f3(): 7457
f4(): 7384

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

另见:

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

另见:
毕竟,这种微优化可以为231次执行节省一到两秒钟的时间。你在现实生活中也经常这么做吗?比起过早的优化,我更喜欢可读性。

为什么执行速度会不同

  • JVM的垃圾收集可能会失败 在一些死刑执行过程中被执行
  • 您的操作系统计划其他 要执行的任务,因此也为其他应用程序提供了资源。甚至优先考虑他们 由于编译do/while和for的方式,可能会有一些差异,但这些应该忽略不计
让我们抓起我的答案


这一定是由于操作系统或Java为windows编译的方式,我已经在windows上对其进行了测试,并在windows上获得了与您相同的结果。

在多次运行后,Hotspot编译器可能会优化每个方法。这需要时间和变化。但由于循环的工作方式大致相同,因此时间最终会变得相似似乎是合理的。

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

f1(): 3234
f2(): 3132
f3(): 3114
f4(): 3089
我猜您的JVM在某些地方没有进行相同的优化

您可以使用javap:
javap-l-cpr1
检查为不同函数生成的字节码。当我这样做时,f2()会得到以下结果:

publicstaticvoidf2();
代码:
0:lconst_0
1:lstore_0
2:invokestatic#2//方法java/lang/System.currentTimeMillis:()J
5:lstore_2
6:ldc2#U w#3//长2147483647l
9:1商店4
11:lload 4
13:2
14:lconst_1
15:lsub
16:1商店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//长2147483647l
35:ladd
36:lstore_0
37:getstatic#5//字段java/lang/System.out:Ljava/io/PrintStream;
40:新的#6//类java/lang/StringBuilder
43:dup
44:特别是#7//方法java/lang/StringBuilder。“”:()V
47:最不发达国家13//字符串f2():
49:invokevirtual#9//方法java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
52:invokestatic#2//方法java/lang/System.currentTimeMillis:()J
55:lload_2
56:lsub
57:invokevirtual#10//方法java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
60:invokevirtual#11//方法java/lang/StringBuilder.toString:()Ljava/lang/String;
63:invokevirtual#12//方法java/io/PrintStream.println:(Ljava/lang/String;)V
66:返回

f2()速度较慢的一个可能原因可能是编译器/JVM在
while(i-->0)
后减量运算符方面不够聪明。基本上,在增量之前和之后都需要
i
的值,因此如果简单地执行此操作,则会涉及更多的工作。

我怀疑这与在具有32位ALU的机器上执行64位算术有关。我怀疑在递增/递减之前/之后的某些测试组合在本机指令级别需要更长的时间,这是由于微妙的流水线效应。有人报告说64位机器上的数字是持平的,这一事实支持了这一理论。确认这一点的方法是获取JIT编译器生成的本机代码的转储,获取特定CPU的文档,并找出时钟周期的去向

但老实说,我不知道这是否值得。我们有明确的证据表明,您的微基准测试数据依赖于CPU,并且所做的“工作”显然不具有代表性。(为什么要在32位机器上使用长循环计数器?)


我也有点惊讶,JIT编译器没有发现循环(在每种情况下)可以完全优化。

这是可复制的还是一次性的?java的规则是:JVM比你聪明,不要尝试和超越它it@Andreas_D可复制的你自己试试代码。@Andrei-如果我手头有编译器,我会的;)-请检查BalusC的第三个列表项,然后再试一次好吗?知道吗,看起来后减量比前减量慢得多,但是-正如BalusC所显示的,它可能是一个“jvm优化工件”
f(3)
的运行时间比
f(2)
少一倍,因为在比较之前会减量。@InsertNickHere请检查链接I
f1(): 3219
f2(): 4859
f3(): 2610
f4(): 3031
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)
f1(): 7495
f2(): 7418
f3(): 7457
f4(): 7384
f1(): 3234
f2(): 3132
f3(): 3114
f4(): 3089
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