Оптимизация Java: скорость внутренних циклов противоречива? - PullRequest
5 голосов
/ 02 февраля 2012

Мой друг и я в тупике. Почему в этих двух блоках кода первый внутренний цикл быстрее, чем второй внутренний цикл? Это какая-то оптимизация 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

Безумие: делать одно и то же снова и снова и получать разные результаты.

Ответы [ 2 ]

4 голосов
/ 02 февраля 2012

Краткий ответ: Чтобы избежать этой проблемы, поместите код, который вы тестируете, в отдельный метод . Разогрейте метод, вызвав его 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 Blog . Чтобы поддержать эту точку зрения, я получаю следующие результаты в зависимости от того, как я запускаю программу: 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
0 голосов
/ 02 февраля 2012

Я получаю разные результаты. Я использую Java 1.7.0_02, и второй цикл немного быстрее первого.

Попробуйте использовать команду "javap -l -c Test", чтобы разобрать файл класса и проверить различия. С помощью компилятора я использую первый цикл, содержащий ifle (ветвь, если значение <= 0), тогда как второй цикл содержит <code>if_icmpge (ветвь, если значение2> = значение1) и iconst_0 перед загрузкой 0 в стек .

...