Есть ли разница в производительности между циклами 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 ГБ
На самом деле он не будет компилироваться, потому что 10000000000 не является int.
+1 за профилирование самостоятельно.
возможный дубликат Есть ли разница в производительности между циклом for и циклом for-each?
какой из них вы называете «современным»? допустим для каждого?
не могли бы вы добавить фигурные скобки {} для пользователей буфера обмена? 8)




Почему бы не измерить это самостоятельно?
Это звучит немного жестко, но такие вопросы очень легко проверить самостоятельно.
Просто создайте массив и выполните каждый цикл 1000 или более раз и измерьте количество времени. Повторить несколько раз для устранения глюков.
Нет никакой разницы. Java преобразует расширенный цикл for в обычный цикл for. Расширение for - это просто «синтаксический сахар». Сгенерированный байт-код одинаков для обоих циклов.
Это неверный ответ. Скомпилируйте оба примера и проверьте полученные байтовые коды. Вы увидите, что код другой, как я объясняю в своем ответе.
Я считаю, что вы не знаете и не должны гадать. Попытки перехитрить компиляторов в наши дни бесплодны.
Были времена, когда люди изучали «Шаблоны», которые, казалось, оптимизировали некоторые операции, но в следующей версии Java эти шаблоны были на самом деле медленнее.
Всегда пишите как можно яснее и не беспокойтесь об оптимизации до тех пор, пока у вас в руках действительно не появится некоторая пользовательская спецификация и вы не сможете выполнить какое-то требование, и даже в этом случае будьте очень осторожны при запуске до и после тестов, чтобы убедиться, что ваш «fix» на самом деле улучшил его настолько, чтобы выполнить это требование.
Компилятор может делать некоторые удивительные вещи, которые действительно взорвут ваши носки, и даже если вы сделаете какой-то тест, который повторяется в каком-то большом диапазоне, он может работать совершенно иначе, если у вас меньший диапазон или измените то, что происходит внутри цикла.
Своевременная компиляция означает, что иногда он может превзойти C, и нет причин, по которым в некоторых случаях он не может превзойти статический язык ассемблера (сборка не может заранее определить, что вызов не требуется, Java иногда может это сделать.
Подводя итог: наибольшая ценность, которую вы можете вложить в свой код, - это написать его так, чтобы он был читабельным.
Я бы добавил, что если он не знает и не должен угадывать, он все равно может спросить или посмотреть. Это не отменяет того факта, что перехитрить компиляторы - это, так сказать, хрупкая вещь, поскольку следующее обновление может вести себя иначе. Но это не значит, что он ни к чему не должен относиться. Если ему не следует вводить эти глубины java для стабильной оптимизации, это может быть связано с тем, что создатели java имели в виду, что он, например, ссылается только на спецификация языка Java.
Ваша рукописная "старая" форма выполняет меньше инструкций и может быть быстрее, хотя вам придется профилировать ее в рамках данного 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, похоже, оптимизировал тонкую разницу в скомпилированном байтовом коде.
Сколько памяти это занимает. 80 ГБ?