为什么 Iterable.sum() 在 Kotlin 中很慢?

Why Iterable.sum() is slow in Kotlin?

我注意到 Itarable.sum() 的性能与使用手动求和的直接 for 循环之间的惊人差异。考虑一下:

import kotlin.system.measureTimeMillis

fun main(args: Array<String>) {
    var sink = 0;
    repeat(5) {
        println(measureTimeMillis {
            var sum = 0
            for (i in 1..10_000_000) {
                sum += i
            }
            sink += sum
        })
    }
    repeat(5) {
        println(measureTimeMillis {
            sink += (1..10_000_000).sum()
        })
    }
}

令人惊讶的是,使用 Iterable.sum() 最多慢 10 倍, 与sum() implementation几乎相同的代码相比。 这是为什么?

更新:

当我以 js 为目标时,sum() 只是稍微慢一点。

measureTimeMillis()可以定义为:

import kotlin.js.Date
public inline fun measureTimeMillis(block: () -> Unit): Double {
    val start = Date.now()
    block()
    return Date.now() - start
}

更新2:

在同一 linux 机器上,jvm sum() 比 js 还要慢。以下是 jvm (Oracle jdk9) 和 js(最新 chrome)的 100_000_000 次迭代的结果:

105   // jvm raw loop
76    // jvm raw loop (jit?)
75    // jvm raw loop (jit?)
75    // jvm raw loop (jit?)
70    // jvm raw loop (jit?)
633   // jvm sum()
431   // jvm sum()
562   // jvm sum()
327   // jvm sum() (jit?)
332   // jvm sum() (jit?)

110   // js raw loop
108   // js raw loop
232   // js raw loop
227   // js raw loop
227   // js raw loop
321   // js sum()
284   // js sum()
264   // js sum()
266   // js sum()
265   // js sum()

所以,在同一台机器上,使用 sum() 时,jvm 似乎比 js 慢。又是一个惊喜。

显然,我们在这里比较的是超级优化的紧密循环。我在 "manual sum" 的重复和 "built-in" 的情况下看到了非常稳定的结果。这表示 GC activity.

启动 VisualVM 并使用其 VisualGC 插件后,我确认在手动总和计算期间没有 GC activity,但在内置案例中有很多。

查看生成的字节码,区别就很明显了:惯用法 for (i in 1..range) { ... } 直接编译成一个计数循环。这实际上是 documented:

Integral type ranges (IntRange, LongRange, CharRange) have an extra feature: they can be iterated over. The compiler takes care of converting this analogously to Java's indexed for-loop, without extra overhead.

不幸的是,相同的优化不适用于扩展函数 Iterable.sum(),因为它必须适用于任何 Iterable。编译器 可以 看到发生了什么并引入另一个内在函数,它可以简单地将整个事物转换为结果总和而无需计算,或者如果范围边界没有硬编码则使用直接公式。

JavaScript 在这里处于类似的地位,因为它也有一个强大的 JIT 编译器。我不能评论任何具体的东西,但它很可能避免在热循环中分配。