I'm working on a game written in Kotlin and was looking into improving GC churn. One of the major sources of churn are for-loops called in the main game/rendering loops that result in the allocation of iterators.
Turning to the documentation, I found this paragraph:
A for loop over an array is compiled to an index-based loop that does not create an iterator object.
If you want to iterate through an array or a list with an index, you can do it this way:
for (i in array.indices) print(array[i])
Note that this “iteration through a range” is compiled down to optimal implementation with no extra objects created.
https://kotlinlang.org/docs/reference/control-flow.html#for-loops
Is this really true? To verify, I took this simple Kotlin program and inspected the generated byte code:
fun main(args: Array<String>) { val arr = arrayOf(1, 2, 3) for (i in arr.indices) { println(arr[i]) } }
According to the quote above, this should not result in any objects allocated, but get compiled down to a good old pre-Java-5 style for-loop. However, what I got was this:
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
This looks to me as if a method called getIndices
is called that allocates a temporary IntRange
object to back up bounds checking in this loop. How is this an "optimal implementation" with "no extra objects created", or am I missing something?
UPDATE: So, after toying around a bit more and looking at the answers, the following appears to be true for Kotlin 1.0.2:
Arrays:
for (i in array.indices)
: range allocationfor (i in 0..array.size)
: no allocationfor (el in array)
: no allocationarray.forEach
: no allocationCollections:
for (i in coll.indices)
range allocationfor (i in 0..coll.size)
: no allocationfor (el in coll)
: iterator allocationcoll.forEach
: iterator allocationThe capacity of an Array is fixed. Whereas ArrayList can increase and decrease size dynamically. An Array is a collection of similar items. Whereas ArrayList can hold item of different types.
The array is a specified-length data structure whereas ArrayList is a variable-length Collection class.
The enhanced for loop (sometimes called a "for each" loop) can be used with any class that implements the Iterable interface, such as ArrayList .
As far as I know the only allocation-less way to define a for
loop is
for (i in 0..count - 1)
All other forms lead to either a Range
allocation or an Iterator
allocation. Unfortunately, you cannot even define an effective reverse for
loop.
To iterate an array without allocating extra objects you can use one of the following ways.
for
-loop for (e in arr) { println(e) }
forEach
extension arr.forEach { println(it) }
forEachIndexed
extension, if you need to know index of each element arr.forEachIndexed { index, e -> println("$e at $index") }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With