Performance 科特林的慢速飞行

Performance 科特林的慢速飞行,performance,kotlin,compiler-construction,Performance,Kotlin,Compiler Construction,我使用以下代码来衡量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 - s

我使用以下代码来衡量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()
}
这就是我得到的结果:

适用范围:84
对于收藏中的:83
forEach:86
每小时范围:294
总数:83

所以我的问题是:为什么每种语法结构的范围要比其他语法结构慢得多
在我看来,编译器可能会在所有情况下生成相等的字节码(但在“range forEach”的情况下不会生成相同的字节码)

最有趣的比较可能是这两种情况之间的比较:

病例A:服用86ms

time("forEach") {
    var sum = 0
    arr.forEach { sum += it }
    sum
}
病例B:服用294ms

time("range forEach") {
    var sum = 0
    (0 until n).forEach { sum += arr[it] }
    sum
}
当案例A实际调用IntArray.forEach(…)时,案例B调用的是
Iterable.forEach(…)
——这是两个不同的调用

我想Kotlin编译器知道优化
IntArray.forEach(…)
,但不知道
Iterable.forEach(…)
,从文档:


正如您所看到的,
forEach
是数组的特殊情况,但是对于所有的
Iterable
都有一个实现,所以它需要创建一个迭代器。

感谢您的代码。我更感兴趣的是数组和列表之间的性能差异,对于运行时间是列表的6倍感到有点震惊。我认为这个列表的创建速度很慢

我的计算机或比较上的以上运行时: 适用范围:55
对于收藏中的:57
forEach:55
每小时范围:223
总数:57

//code change
//val arr = IntArray(n) { rand.nextInt() }
val arr = List(n) { rand.nextInt() }
适用范围:383
对于收藏中的:367
forEach:367
每小时范围:486

sum:371

两个
forEach
都是
内联的
,在运行时没有创建lambda的实例。另外,
IntArray.forEach
是最快的一个。@AlexeyRomanov让我理解:“forEach”调用是内联的,但是是一个lambda表达式<代码>{sum+=it}仍然需要调用。内联函数中lambda的应用程序是内联的,它直接在方法代码中结束。因此,这是否意味着在“forEach”使用IntArray迭代器的情况下,它与没有迭代器的基于索引的循环一样快?否,
IntArray.forEach
的代码使用基于索引的循环。
Iterable.forEach
的代码不能(或者从技术上讲可以,但对于许多类型来说都会非常慢)。好的,谢谢。据我所知,答案是“因为没有针对IntRange的优化forEach版本”。是的,Kotlin开发人员(或您!)可以专门为
范围
添加扩展方法,而且应该同样快。(虽然我没有检查,这取决于“for loop over a Range”优化是否适用于非文字范围).但要注意JVM预热时间。方法第一次运行时,JVM通常不会将其重新编译为机器代码。通常,第二次调用同一个方法时,它的运行速度有时会提高很多倍。