2

哪个函数更快(p 是 MyObject 的 atomic public int 属性):

public static boolean check(MyObject o1, List<MyObject> list) {

    int p = o1.p;
    for (int i = 0; i < 1000000; i++) {
        MyObject o = list.get(i);
        if (o.p < p) return false;
    }

    return true;
}

或者

public static boolean check(MyObject o1, List<MyObject> list) {

    for (int i = 0; i < 1000000; i++) {
        MyObject o = list.get(i);
        if (o.p < o1.p) return false;
    }

    return true;
}

那么通过使用局部变量 p 我们缓存对象属性调用还是由编译器优化内联完成?

4

3 回答 3

2

编译器javac几乎没有优化。

然而,JIT 可以显着优化代码,包括将字段存储在寄存器中,

于 2012-08-13T19:40:56.457 回答
2

虽然不是直接在主题上,但您可以通过使用“foreach”循环来改进样式并实现更恒定的速度:

for (MyObject o : list) {
    if (o.p < o1.p) return false;
}

切换到 foreach 的测试结果:

  • ArrayList慢 10%
  • LinkedList仅使用 100000 个元素,速度提高 700 倍
于 2012-08-13T19:47:05.630 回答
1

简短的回答:这取决于

稍长 答案:这取决于编译器、VM 和 VM 的设置。

背景:在服务器模式下使用 HotSpot VM(最常见的风格)将使两种变体相等,因为 VM 在服务器模式下会循环不变提升。在客户端模式下,如果 VM 认为值得优化,这可能会完成可能不会完成,甚至可以稍后完成。

循环不变提升是循环优化之一,它在大多数现代编译器(或者在 Java 的情况下,VM)中实现。至于 javac 生成的代码:如果 VM 没有进一步优化,您的第一个代码片段将执行得更快。

public static boolean check(Test$MyObject, java.util.List);
  Code:
   0:   aload_0
   1:   getfield        #7; //Field Test$MyObject.p:I
   4:   istore_2
   5:   iconst_0
   6:   istore_3
   7:   iload_3
   8:   ldc     #4; //int 1000000
   10:  if_icmpge       42
   13:  aload_1
   14:  iload_3
   15:  invokeinterface #11,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   20:  checkcast       #5; //class Test$MyObject
   23:  astore  4
   25:  aload   4
   27:  getfield        #7; //Field Test$MyObject.p:I
   30:  iload_2
   31:  if_icmpge       36
   34:  iconst_0
   35:  ireturn
   36:  iinc    3, 1
   39:  goto    7
   42:  iconst_1
   43:  ireturn

--

public static boolean check(Test$MyObject, java.util.List);
  Code:
   0:   iconst_0
   1:   istore_2
   2:   iload_2
   3:   ldc     #4; //int 1000000
   5:   if_icmpge       38
   8:   aload_1
   9:   iload_2
   10:  invokeinterface #11,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   15:  checkcast       #5; //class Test$MyObject
   18:  astore_3
   19:  aload_3
   20:  getfield        #7; //Field Test$MyObject.p:I
   23:  aload_0
   24:  getfield        #7; //Field Test$MyObject.p:I
   27:  if_icmpge       32
   30:  iconst_0
   31:  ireturn
   32:  iinc    2, 1
   35:  goto    2
   38:  iconst_1
   39:  ireturn

如您所见,第二个示例中第 20 行的 getfield 操作位于第一个示例中的第 1 行,并且位于循环之外(变体 1 中的第 7 到 39 行和变体 2 中的第 2 到 35 行),因此仅执行ondced 而不是 1000000 次。

于 2012-08-15T11:03:39.067 回答