5

我和我的朋友都被难住了。在这两个代码块中,为什么第一个内循环比第二个内循环快?这是某种 JVM 优化吗?

public class Test {
   public static void main(String[] args) {
      int[] arr = new int[100000000];
      arr[99999999] = 1;
      long t1, t2, t3;
      for (int ndx = 0; ndx < 10; ndx++) {
         t1 = System.currentTimeMillis();
         for (int i = 0; i < arr.length; i++)
            if (0 < arr[i])
               System.out.print("");

         t2 = System.currentTimeMillis();
         for (int i = 0; i < arr.length; i++)
            if (arr[i] > 0)
               System.out.print("");

         t3 = System.currentTimeMillis();
         System.out.println(t2 - t1 +" "+(t3 - t2));
      }
   }
}

结果:

me@myhost ~ $ java Test
57 80
154 211
150 209
149 209
150 209
150 209
151 209
150 210
150 210
149 209

交换不等式的顺序:

public class Test {
   public static void main(String[] args) {
      int[] arr = new int[100000000];
      arr[99999999] = 1;
      long t1, t2, t3;
      for (int ndx = 0; ndx < 10; ndx++) {
         t1 = System.currentTimeMillis();
         for (int i = 0; i < arr.length; i++)
            if (arr[i] > 0)
               System.out.print("");

         t2 = System.currentTimeMillis();
         for (int i = 0; i < arr.length; i++)
            if (0 < arr[i])
               System.out.print("");

         t3 = System.currentTimeMillis();
         System.out.println((t2 - t1) +" "+(t3 - t2));
      }
   }
}

结果:

me@myhost ~ $ java Test
56 80
155 210
150 209
149 209
151 210
149 209
150 209
149 208
149 209
149 208

精神错乱:一遍又一遍地做同样的事情并得到不同的结果。

4

2 回答 2

4

简短回答:为避免此问题,请将您正在测试的代码放在单独的方法中。在计时之前调用该方法 11,000 次来预热该方法。这 2 个将允许 JIT 编译器将方法与已编译版本交换。与 -server 一起运行,它只是更好地调整。使用 System.nanoTime() 来计时。使用以下代码,您将获得一致的测量结果。

public class AlphaTest
{
public static void processA(int[] arr)
{
    for (int i = 0; i < arr.length; i++)
        if (arr[i] > 0)
            System.out.print("");
}

public static void processB(int[] arr)
{
    for (int i = 0; i < arr.length; i++)
        if (0 < arr[i])
            System.out.print("");
}

public static void main(String[] args)
{
    int[] smallArr = new int[10];
    for (int i = 0; i < smallArr.length; i++)
    {
        smallArr[i] = 1;
    }
    //warmup
    for (int i = 0; i < 11000; i++)
    {
        processA(smallArr);
        processB(smallArr);
    }

    int[] arr = new int[100000000];
    arr[99999999] = 1;
    long t1, t2, t3;
    for (int ndx = 0; ndx < 10; ndx++)
    {
        t1 = System.nanoTime();
        processA(arr);

        t2 = System.nanoTime();
        processB(arr);

        t3 = System.nanoTime();
        System.out.println(((t2 - t1)/1000000L) + " " + ((t3 - t2)/1000000L));
    }
}
}

长答案:这绝对是马特在评论中指出的“微基准测试”问题。请参阅Azul 博客。为了支持这一观点,我得到以下结果,具体取决于我运行程序的方式: as -client as -server 并且在禁用 JIT 的情况下,每个设置只有 2 个结果行,其余的类似。

java -client -Xms1024m -Xmx1024m Test
272 262
263 252
...
java -server -Xms1024m -Xmx1024m Test
513 173
483 201
...
java -client -Djava.compiler=NONE -Xms1024m -Xmx1024m AlphaTest
2062 1929
2042 2034
...
java -server -Djava.compiler=NONE -Xms1024m -Xmx1024m AlphaTest
1844 1864
1843 1931
于 2012-02-02T16:10:53.007 回答
0

我得到不同的结果。我使用的是 Java 1.7.0_02,第二个循环比第一个循环稍快。

尝试使用“javap -l -c Test”命令反汇编类文件并检查差异。对于编译器,我使用的第一个循环包含ifle(branch if value <= 0) 而第二个循环包含if_icmpge(branch if value2 >= value1) 并iconst_0在它之前将 0 加载到堆栈中。

于 2012-02-02T10:47:45.393 回答