Какова стоимость вызова 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 должен видеть это в обоих случаях. Я ожидаю, что в обоих случаях полученный машинный код будет одинаковым. По крайней мере, для компилятора сервера.