Slow range forEach in Kotlin

Viewed 1876

I used the following code to measure performance of different syntax constructions in Kotlin

fun time(what: String, body: () -> Int) {
    val start = System.currentTimeMillis()
    var sum = 0

    repeat(10) {
        sum += body()
    }

    val end = System.currentTimeMillis()

    println("$what: ${(end - start) / 10}")
}

val n = 200000000
val rand = Random()
val arr = IntArray(n) { rand.nextInt() }

time("for in range") {
    var sum = 0
    for (i in (0 until n))
        sum += arr[i]
    sum
}

time("for in collection") {
    var sum = 0
    for (x in arr)
        sum += x
    sum
}

time("forEach") {
    var sum = 0
    arr.forEach { sum += it }
    sum
}

time("range forEach") {
    var sum = 0
    (0 until n).forEach { sum += arr[it] }
    sum
}

time("sum") {
    arr.sum()
}

And that is the result I got:

for in range: 84
for in collection: 83
forEach: 86
range forEach: 294
sum: 83

So my question is: Why is range forEach much slower that other syntax constructions?
It seems to me that compiler may generate equal bytecode in all cases (but does not in case of "range forEach")

3 Answers

The most interesting comparison is probably between these 2 cases:

Case A: taking 86 ms

time("forEach") {
    var sum = 0
    arr.forEach { sum += it }
    sum
}

Case B: taking 294 ms

time("range forEach") {
    var sum = 0
    (0 until n).forEach { sum += arr[it] }
    sum
}

While case A is actually calling IntArray.forEach(...) case B is calling Iterable<T>.forEach(...) - these are two different calls.

I guess the Kotlin compiler knows to optimize IntArray.forEach(...), but not Iterable<T>.forEach(...).

Thanks for this code. I was more interested in the performance difference between array and list and was a bit shocked to have 6 times as long running time. I thought the list is only slow in creation.

Above runtimes on my machine for comparison:

for in range: 55
for in collection: 57
forEach: 55
range forEach: 223
sum: 57

//code change replace IntArray with List
//val arr = IntArray(n) { rand.nextInt() }
val arr = List(n) { rand.nextInt() }

for in range: 383
for in collection: 367
forEach: 367
range forEach: 486
sum: 371

//code change replace IntArray with Array<Int>
//val arr = IntArray(n) { rand.nextInt() }
val arr: Array<Int> = Array<Int>(n) { rand.nextInt() }

for in range: 331
for in collection: 281
forEach: 276
range forEach: 445
sum: 313

So, the main difference is not the use Array vs List but the use of Object Int vs primitive type

Related