Kotlin 中的 forEach 范围较慢
Slow range forEach in Kotlin
我使用以下代码来测量 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()
}
这就是我得到的结果:
for in range: 84
for in collection: 83
forEach: 86
range forEach: 294
sum: 83
所以我的问题是:为什么 range forEach 比其他语法结构慢得多?
在我看来,编译器可能会在所有情况下生成相同的字节码(但在 "range forEach" 的情况下不会)
最有趣的比较可能是这两种情况:
案例 A:耗时 86 毫秒
time("forEach") {
var sum = 0
arr.forEach { sum += it }
sum
}
案例 B:耗时 294 毫秒
time("range forEach") {
var sum = 0
(0 until n).forEach { sum += arr[it] }
sum
}
虽然案例 A 实际上正在调用 IntArray.forEach(...)
案例 B 正在调用 Iterable<T>.forEach(...)
- 这是两个不同的调用。
我猜 Kotlin 编译器知道优化 IntArray.forEach(...)
,但不知道 Iterable<T>.forEach(...)
。
来自文档:
forEach
正如您在 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html 看到的那样,它是数组的特殊情况,但对所有 Iterable
有一个单一的实现,因此它需要创建一个迭代器。
感谢您提供此代码。我对数组和列表之间的性能差异更感兴趣,并且 运行 的时间是原来的 6 倍,这让我感到有些震惊。我以为这个列表只是创建速度慢。
我机器上的以上运行时间用于比较:
范围内:55
因为 collection: 57
对于每个:55
每个范围:223
总和:57
//code change replace IntArray with List
//val arr = IntArray(n) { rand.nextInt() }
val arr = List(n) { rand.nextInt() }
范围内:383
因为 collection: 367
对于每个:367
每个范围:486
总和:371
//code change replace IntArray with Array<Int>
//val arr = IntArray(n) { rand.nextInt() }
val arr: Array<Int> = Array<Int>(n) { rand.nextInt() }
在范围内:331
对于 collection: 281
对于每个:276
每个范围:445
总和:313
因此,主要区别不在于使用 Array 与 List,而是使用 Object Int 与原始类型
我使用以下代码来测量 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()
}
这就是我得到的结果:
for in range: 84
for in collection: 83
forEach: 86
range forEach: 294
sum: 83
所以我的问题是:为什么 range forEach 比其他语法结构慢得多?
在我看来,编译器可能会在所有情况下生成相同的字节码(但在 "range forEach" 的情况下不会)
最有趣的比较可能是这两种情况:
案例 A:耗时 86 毫秒
time("forEach") {
var sum = 0
arr.forEach { sum += it }
sum
}
案例 B:耗时 294 毫秒
time("range forEach") {
var sum = 0
(0 until n).forEach { sum += arr[it] }
sum
}
虽然案例 A 实际上正在调用 IntArray.forEach(...)
案例 B 正在调用 Iterable<T>.forEach(...)
- 这是两个不同的调用。
我猜 Kotlin 编译器知道优化 IntArray.forEach(...)
,但不知道 Iterable<T>.forEach(...)
。
来自文档:
forEach
正如您在 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html 看到的那样,它是数组的特殊情况,但对所有 Iterable
有一个单一的实现,因此它需要创建一个迭代器。
感谢您提供此代码。我对数组和列表之间的性能差异更感兴趣,并且 运行 的时间是原来的 6 倍,这让我感到有些震惊。我以为这个列表只是创建速度慢。
我机器上的以上运行时间用于比较:
范围内:55
因为 collection: 57
对于每个:55
每个范围:223
总和:57
//code change replace IntArray with List
//val arr = IntArray(n) { rand.nextInt() }
val arr = List(n) { rand.nextInt() }
范围内:383
因为 collection: 367
对于每个:367
每个范围:486
总和:371
//code change replace IntArray with Array<Int>
//val arr = IntArray(n) { rand.nextInt() }
val arr: Array<Int> = Array<Int>(n) { rand.nextInt() }
在范围内:331
对于 collection: 281
对于每个:276
每个范围:445
总和:313
因此,主要区别不在于使用 Array 与 List,而是使用 Object Int 与原始类型