Современный цикл for для примитивного массива

Есть ли разница в производительности между циклами for в примитивном массиве?

Предполагать:

double[] doubleArray = new double[300000];


for (double var: doubleArray) 
   someComplexCalculation(var);

или же :

for ( int i = 0, y = doubleArray.length; i < y; i++)
   someComplexCalculation(doubleArray[i]);

Результат испытаний

Я на самом деле профилировал это:

Total timeused for modern loop= 13269ms
Total timeused for old loop   = 15370ms

Таким образом, современный цикл на самом деле работает быстрее, по крайней мере, на моей Mac OSX JVM 1.5.

Сколько памяти это занимает. 80 ГБ?

JesperE 05.11.2008 23:52

Я вообще-то не думал, когда писал это ... но да. 80 ГБ

Dan 06.11.2008 00:06

На самом деле он не будет компилироваться, потому что 10000000000 не является int.

Dave L. 06.11.2008 03:28

+1 за профилирование самостоятельно.

Aaron Digulla 11.05.2012 12:59

какой из них вы называете «современным»? допустим для каждого?

n611x007 02.10.2013 20:11

не могли бы вы добавить фигурные скобки {} для пользователей буфера обмена? 8)

n611x007 02.10.2013 21:37
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
13
7
7 829
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Почему бы не измерить это самостоятельно?

Это звучит немного жестко, но такие вопросы очень легко проверить самостоятельно.

Просто создайте массив и выполните каждый цикл 1000 или более раз и измерьте количество времени. Повторить несколько раз для устранения глюков.

Нет никакой разницы. Java преобразует расширенный цикл for в обычный цикл for. Расширение for - это просто «синтаксический сахар». Сгенерированный байт-код одинаков для обоих циклов.

Это неверный ответ. Скомпилируйте оба примера и проверьте полученные байтовые коды. Вы увидите, что код другой, как я объясняю в своем ответе.

erickson 06.11.2008 00:20

Я считаю, что вы не знаете и не должны гадать. Попытки перехитрить компиляторов в наши дни бесплодны.

Были времена, когда люди изучали «Шаблоны», которые, казалось, оптимизировали некоторые операции, но в следующей версии Java эти шаблоны были на самом деле медленнее.

Всегда пишите как можно яснее и не беспокойтесь об оптимизации до тех пор, пока у вас в руках действительно не появится некоторая пользовательская спецификация и вы не сможете выполнить какое-то требование, и даже в этом случае будьте очень осторожны при запуске до и после тестов, чтобы убедиться, что ваш «fix» на самом деле улучшил его настолько, чтобы выполнить это требование.

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

Своевременная компиляция означает, что иногда он может превзойти C, и нет причин, по которым в некоторых случаях он не может превзойти статический язык ассемблера (сборка не может заранее определить, что вызов не требуется, Java иногда может это сделать.

Подводя итог: наибольшая ценность, которую вы можете вложить в свой код, - это написать его так, чтобы он был читабельным.

Я бы добавил, что если он не знает и не должен угадывать, он все равно может спросить или посмотреть. Это не отменяет того факта, что перехитрить компиляторы - это, так сказать, хрупкая вещь, поскольку следующее обновление может вести себя иначе. Но это не значит, что он ни к чему не должен относиться. Если ему не следует вводить эти глубины java для стабильной оптимизации, это может быть связано с тем, что создатели java имели в виду, что он, например, ссылается только на спецификация языка Java.

n611x007 02.10.2013 21:07
Ответ принят как подходящий

Ваша рукописная "старая" форма выполняет меньше инструкций и может быть быстрее, хотя вам придется профилировать ее в рамках данного JIT-компилятора, чтобы знать наверняка. «Новая» форма определенно на нет быстрее.

Если вы посмотрите на дизассемблированный код (скомпилированный Sun JDK 1.5), вы увидите, что «новая» форма эквивалентна следующему коду:

1: double[] tmp = doubleArray;
2: for (int i = 0, y = tmp.length; i < y; i++) {
3:   double var = tmp[i];
4:   someComplexCalculation(var);
5: }

Итак, вы можете видеть, что используется больше локальных переменных. Присвоение doubleArray к tmp в строке 1 является «лишним», но оно не происходит в цикле и, вероятно, не может быть измерено. Назначение var в строке 3 также является дополнительным. Если есть разница в производительности, это будет ответственным.

Строка 1 может показаться ненужной, но это шаблон для кеширования результата, если массив вычисляется методом перед входом в цикл.

Тем не менее, я бы использовал новую форму, если вам не нужно что-то делать с переменной index. Любая разница в производительности, вероятно, будет оптимизирована JIT-компилятором во время выполнения, и новая форма более понятна. Если вы продолжите делать это «вручную», вы можете пропустить будущие оптимизации. Как правило, хороший компилятор может хорошо оптимизировать «глупый» код, но натыкается на «умный» код.

Мне очень любопытен ваш вопрос, даже после моего предыдущего ответа. Вот и решил сам проверить. Я написал этот небольшой фрагмент кода (пожалуйста, не обращайте внимания на математическую корректность при проверке того, является ли число простым ;-)):

public class TestEnhancedFor {

    public static void main(String args[]){
        new TestEnhancedFor();
    }

    public TestEnhancedFor(){
        int numberOfItems = 100000;
        double[] items = getArrayOfItems(numberOfItems);
        int repetitions = 0;
        long start, end;

        do {
            start = System.currentTimeMillis();
            doNormalFor(items);
            end = System.currentTimeMillis();
            System.out.printf("Normal For. Repetition %d: %d\n", 
                    repetitions, end-start);

            start = System.currentTimeMillis();
            doEnhancedFor(items);
            end = System.currentTimeMillis();
            System.out.printf("Enhanced For. Repetition %d: %d\n\n", 
                    repetitions, end-start);

        } while (++repetitions < 5);
    }

    private double[] getArrayOfItems(int numberOfItems){
        double[] items = new double[numberOfItems];
        for (int i=0; i < numberOfItems; i++)
            items[i] = i;
        return items;
    }

    private void doSomeComplexCalculation(double item){
        // check if item is prime number
        for (int i = 3; i < item / 2; i+=2){
            if ((item / i) == (int) (item / i)) break;
        }
    }

    private void doNormalFor(double[] items){
        for (int i = 0; i < items.length; i++)
            doSomeComplexCalculation(items[i]);
    }

    private void doEnhancedFor(double[] items){
        for (double item : items)
            doSomeComplexCalculation(item);
    }

}

Запуск приложения дал мне следующие результаты:

Normal For. Repetition 0: 5594 Enhanced For. Repetition 0: 5594

Normal For. Repetition 1: 5531 Enhanced For. Repetition 1: 5547

Normal For. Repetition 2: 5532 Enhanced For. Repetition 2: 5578

Normal For. Repetition 3: 5531 Enhanced For. Repetition 3: 5531

Normal For. Repetition 4: 5547 Enhanced For. Repetition 4: 5532

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

Среднее время (без учета первого повторения) составляет 5535,25 мс для нормального цикла и 5547 мс для расширенного цикла. Но мы можем видеть, что лучшее время работы для обоих циклов одинаково (5531 мс), поэтому я думаю, мы можем прийти к выводу, что оба цикла имеют одинаковую производительность, а различия в прошедшем времени связаны с другими приложениями (даже ОС) машины.

Интересные результаты! Как я и ожидал, JIT, похоже, оптимизировал тонкую разницу в скомпилированном байтовом коде.

erickson 07.11.2008 08:24

Другие вопросы по теме