Какова стоимость вызова array.length
Во время обновления для циклов для каждого цикла в нашем приложении я сталкивался с множеством этих "шаблонов":
for (int i = 0, n = a.length; i < n; i++) {
...
}
вместо
for (int i = 0; i < a.length; i++) {
...
}
Я вижу, что вы получаете производительность для коллекций, потому что вам не нужно вызывать метод size() с каждым циклом. Но с массивами??
Итак, возник вопрос: array.length
дороже обычной?
Ответы
Ответ 1
Нет, вызов array.length
- это O(1)
или операция с постоянным временем.
Так как .length
является (действует как) a public
final
членом array
, доступ к нему медленнее, чем локальная переменная. (Это очень отличается от вызова метода типа size()
)
Современный компилятор, скорее всего, оптимизирует вызов .length
в любом случае.
Ответ 2
У меня было немного времени на обед:
public static void main(String[] args) {
final int[] a = new int[250000000];
long t;
for (int j = 0; j < 10; j++) {
t = System.currentTimeMillis();
for (int i = 0, n = a.length; i < n; i++) { int x = a[i]; }
System.out.println("n = a.length: " + (System.currentTimeMillis() - t));
t = System.currentTimeMillis();
for (int i = 0; i < a.length; i++) { int x = a[i]; }
System.out.println("i < a.length: " + (System.currentTimeMillis() - t));
}
}
Результаты:
n = a.length: 672
i < a.length: 516
n = a.length: 640
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 640
i < a.length: 532
n = a.length: 640
i < a.length: 531
n = a.length: 641
i < a.length: 516
n = a.length: 656
i < a.length: 531
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
Примечания:
- Если вы отмените тесты, то
n = a.length
будет отображаться быстрее, чем i < a.length
примерно наполовину, вероятно, из-за сборки мусора (?).
- Я не мог сделать
250000000
намного больше, потому что получил OutOfMemoryError
в 270000000
.
Точка есть, и это та, которую все еще делали, вам нужно запустить Java из памяти, и вы все еще не видите существенной разницы в скорости между двумя альтернативами. Проведите время разработки на самом деле.
Ответ 3
Я сомневаюсь, что есть какие-то существенные различия, и даже если бы это было так, я бы поспорил, что он, вероятно, оптимизировался во время компиляции. Вы теряете время, когда пытаетесь микро-оптимизировать такие вещи. Сначала сначала прочитайте и исправьте код, а затем, если у вас возникла проблема с производительностью, используйте профайлер, а затем подумайте о выборе лучших структур данных/алгоритмов, если это необходимо, затем беспокоиться об оптимизации детали, которые ваш профилировщик подчеркивает.
Ответ 4
Длина массива сохраняется как переменная-член массива (не такая же, как элемент) в Java, поэтому выборка этой длины является постоянной операцией времени, так же, как чтение переменной-члена из обычного класса. Многие старые языки, такие как C и С++, не сохраняют длину как часть массива, поэтому вы хотите сохранить длину до начала цикла. Вам не нужно делать это на Java.
Ответ 5
В этом случае, почему бы вам не сделать обратный цикл:
for (int i = a.length - 1; i >= 0; --i) {
...
}
Здесь есть 2 микрооптимизации:
- Переключение цикла
- Постфикс декремент
Ответ 6
Это может быть немного быстрее, чтобы сохранить его в переменной. Но я был бы очень удивлен, если бы профайлер указал на это как на проблему.
На уровне байт-кода получение длины массива выполняется с помощью байт-кода arraylength
. Я не знаю, медленнее, чем байт-код iload
, но не должно быть достаточно разницы, чтобы заметить.
Ответ 7
Этот ответ с точки зрения С#, но я думаю, что то же самое относится к Java.
В С# идиома
for (int i = 0; i < a.length; i++) { ...}
распознается как итерация по массиву, поэтому проверка границ исключается при доступе к массиву в цикле, а не к доступу каждого массива.
Это может быть или не быть распознано с кодом, например:
for (int i = 0, n = a.length; i < n; i++) { ...}
или
n = a.length;
for (int i = 0; i < n; i++) { ...}
Сколько из этой оптимизации выполняется компилятором и JITter, которого я не знаю, и, в частности, если он выполняется JITter, я ожидаю, что все 3 будут генерировать один и тот же собственный код.
Однако, первая форма также, вероятно, более читаема людьми, поэтому я бы сказал, идите с этим.
Ответ 8
Array.length является константой, и компилятор JIT должен видеть это в обоих случаях. Я ожидаю, что в обоих случаях полученный машинный код будет одинаковым. По крайней мере, для компилятора сервера.