современный цикл for для примитивного массива - PullRequest
12 голосов
/ 05 ноября 2008

Есть ли разница в производительности между циклами 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

Ответы [ 5 ]

5 голосов
/ 06 ноября 2008

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

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

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

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

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

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

4 голосов
/ 06 ноября 2008

Ваша рукописная, "старая" форма выполняет меньше инструкций и может быть быстрее, хотя вам наверняка придется профилировать ее под конкретным 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 во время выполнения, и новая форма более понятна. Если вы продолжите делать это «вручную», вы можете пропустить дальнейшую оптимизацию. Как правило, хороший компилятор может хорошо оптимизировать «глупый» код, но натыкается на «умный» код.

2 голосов
/ 05 ноября 2008

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

Звучит немного грубо, но подобные вопросы очень легко проверить самим.

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

1 голос
/ 06 ноября 2008

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

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);
    }

}

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

Нормальный Для. Повтор 0: 5594 Улучшено для. Повтор 0: 5594

Нормальный Для. Повторение 1: 5531 Улучшено для. Повторение 1: 5547

Нормальный Для. Повторение 2: 5532 Улучшено для. Повторение 2: 5578

Нормальный Для. Повторение 3: 5531 Улучшено для. Повторение 3: 5531

Нормальный Для. Повторение 4: 5547 Улучшено для. Повторение 4: 5532

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

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

1 голос
/ 05 ноября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...