Array/List iteration без дополнительных распределений объектов

Я работаю над игрой, написанной в Котлине, и изучал улучшение оттока GC. Один из основных источников оттока - это петли, называемые в основных циклах игры/рендеринга, которые приводят к распределению итераторов.

Переходя к документации, я нашел этот абзац:

А для цикла по массиву компилируется в цикл, основанный на индексе, который не создает объект итератора.

Если вы хотите итерации по массиву или списку с индексом, вы можете сделать это следующим образом:

for (i in array.indices)
  print(array[i])

Обратите внимание, что эта "итерация через диапазон" скомпилирована до оптимальной реализации без создания дополнительных объектов.

https://kotlinlang.org/docs/reference/control-flow.html#for-loops

Это правда? Чтобы проверить, я взял эту простую программу Kotlin и проверил сгенерированный байт-код:

fun main(args: Array<String>) {
    val arr = arrayOf(1, 2, 3)
    for (i in arr.indices) {
        println(arr[i])
    }
}

Согласно приведенной выше цитате, это не должно приводить к назначению каких-либо объектов, но скомпилировать их до старого старого стиля pre-Java-5 для цикла. Однако я получил следующее:

      41: aload_1
      42: checkcast     #23                 // class "[Ljava/lang/Object;"
      45: invokestatic  #31                 // Method kotlin/collections/ArraysKt.getIndices:([Ljava/lang/Object;)Lkotlin/ranges/IntRange;
      48: dup
      49: invokevirtual #37                 // Method kotlin/ranges/IntRange.getFirst:()I
      52: istore_2
      53: invokevirtual #40                 // Method kotlin/ranges/IntRange.getLast:()I
      56: istore_3
      57: iload_2
      58: iload_3
      59: if_icmpgt     93

Это выглядит так, как будто вызывается метод под названием getIndices, который выделяет временный объект IntRange для резервного копирования границ в этом цикле. Как это "оптимальная реализация" с "никакими добавленными объектами", или я чего-то не хватает?

UPDATE: Итак, после многого другого и поиска ответов, для Kotlin 1.0.2 выглядит следующее:

Массивы:

  • for (i in array.indices): распределение диапазона
  • for (i in 0..array.size): нет распределения
  • for (el in array): нет распределения
  • array.forEach: нет распределения

Коллекции:

  • for (i in coll.indices) выделение диапазона
  • for (i in 0..coll.size): отсутствие распределения
  • for (el in coll): размещение итератора
  • coll.forEach: распределение итератора

Ответы

Ответ 1

Насколько я знаю, единственный способ определения без привязки for:

for (i in 0..count - 1)

Все остальные формы приводят либо к распределению Range, либо к распределению Iterator. К сожалению, вы даже не можете определить эффективный обратный цикл for.

Ответ 2

Для итерации массива без выделения дополнительных объектов вы можете использовать один из следующих способов.

  • for -loop
    for (e in arr) {
        println(e)
    }
  1. forEach расширение
    arr.forEach {
        println(it)
    }
  1. forEachIndexed расширение, если вам нужно знать индекс каждого элемента
    arr.forEachIndexed { index, e ->
        println("$e at $index")
    }