Эффективность вложенного цикла - PullRequest
7 голосов
/ 31 марта 2010

См. Следующий фрагмент:

    Long first_begin = System.currentTimeMillis();

    // first nested loops
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 1000000; j++) {
            // do some stuff
        }
    }
    System.out.println(System.currentTimeMillis() - first_begin);
    // second nested loops
    Long seconde_begin = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        for (int j = 0; j < 10; j++) {
            // do some stuff
        }
    }
    System.out.println(System.currentTimeMillis() - seconde_begin);

Мне интересно, почему первый вложенный цикл работает медленнее, чем второй?

Привет! * * 1006

Важное примечание! : Мне очень жаль, что я случайно сделал переменную j, начинающуюся с 1, когда этот вопрос впервые задается, я внес исправление.

Обновление: в циклах нет какой-либо определенной логики, я просто провожу какой-то тест, на самом деле, этот вопрос задают во время интервью, и интервьюер подсказывает мне изменить порядок циклов для достижения лучшей производительности. Кстати, я использую JDK1.5. после некоторого теста я теперь более смущен, потому что результат программы не согласован - иногда первый цикл работает быстрее, чем второй, но большую часть времени он работает медленнее, чем второй.

Ответы [ 4 ]

6 голосов
/ 31 марта 2010

РЕДАКТИРОВАТЬ: оригинальный ответ ниже. Теперь, когда вы исправили пример, так что все переменные цикла начинаются с 0, мы снова не имеем достаточно информации. Кажется, вероятно , что это проблема когерентности / местоположения кеша - но мы просто догадываемся. Если бы вы могли предоставить короткую, но полную программу, которая демонстрирует проблему, это помогло бы ... как сказать нам, с какого языка / платформы мы говорим для начала!


Первый цикл имеет 10 * 999999 = 9999990 итераций. Второй цикл имеет 1000000 * 9 = 9000000 итераций. Поэтому я бы ожидал (при прочих равных условиях), что первый цикл займет больше времени.

Однако вы не указали, какую работу вы делаете или на какой платформе это происходит. Есть много вещей, которые могут повлиять на вещи:

  • Второй цикл может поразить кеш лучше
  • Если вы используете JIT-скомпилированную платформу, JIT, возможно, выбрал более интенсивную оптимизацию второго цикла.
  • Операции, которые вы выполняете, сами могут иметь кеширование или что-то в этом роде
  • Если вы выполняете небольшой объем работы, но сначала необходимо загрузить и инициализировать группу типов, это может привести к замедлению первого цикла
4 голосов
/ 31 марта 2010

Этот ответ на обновленный вопрос:

  • Если вы обращаетесь к двумерному массиву, например int[][], то с большим значением во внутреннем цикле должно быть медленнее. Не очень, но все же. Чтобы хоть немного понять проблему, прочитайте о Шлемиэле, уличном художнике , в одном из постов Джоэла в блоге.
  • Причина, по которой вы получаете противоречивые результаты, заключается в том, что вы не выполняете прогрев JVM. JVM постоянно анализирует выполняемый байт-код и оптимизирует его, обычно только после 30-50 итераций он работает с оптимальной скоростью. Да, это означает, что вам нужно сначала запустить код пару десятков раз, а затем сравнить его со средними показателями из еще нескольких десятков запусков из-за сборщика мусора, который замедлит пару запусков.
  • Общее примечание: использование Long объекта вместо long примитива просто глупо, JVM, скорее всего, оптимизирует его, заменив его на примитивный, если он может, а если нет, обязательно будет несколько ( хотя и крайне незначительное ) постоянное замедление от его использования.
2 голосов
/ 31 марта 2010

Если вы посмотрите на сгенерированный байт-код, два цикла практически идентичны. ИСКЛЮЧИТЬ, что при выполнении условия while для цикла 10 Java получает 10 как непосредственное значение из инструкции, но когда оно выполняет условие while для цикла 1000000, Java загружает 1000000 из переменной. У меня нет никакой информации о том, сколько времени требуется для выполнения каждой инструкции, но кажется вероятным, что немедленная загрузка будет быстрее, чем загрузка из переменной.

Обратите внимание, что в первом цикле сравнение с 1000000 должно выполняться 10 миллионов раз, в то время как во втором цикле оно выполняется только 1 миллион раз. Конечно, сравнение с 10 выполняется гораздо чаще во втором цикле, но если переменная загрузка намного медленнее, чем немедленная загрузка, это объясняет результаты, которые вы видите.

2 голосов
/ 31 марта 2010

Вопрос сдвинут. Это не те дроиды, которых ты ищешь ...

Потому что в первом примере вы выполняете в ~ 1000000 раз больше работы. ; -)

...