当前位置 : 主页 > 网络安全 > 测试自动化 >

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

来源:互联网 收集:自由互联 发布时间:2021-06-22
我注意到Itarable.sum()的性能与手动求和的直接for循环之间存在惊人的差异.考虑一下: import kotlin.system.measureTimeMillisfun main(args: ArrayString) { var sink = 0; repeat(5) { println(measureTimeMillis { var su
我注意到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
}

UPDATE2:

在同一台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慢.又一个惊喜.

显然,我们在这里比较超优化的紧密循环.对于“内置”案例中的“手动求和”和狂野差异,我看到了相当稳定的结果.这表明GC活动.

在启动VisualVM并使用其VisualGC插件时,我确认在手动求和计算期间没有GC活动,但在内置的情况下有很多.

查看生成的字节码,差异变得明显:(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编译器.我无法评论任何具体的内容,但它最有可能避免在热循环中进行分配.

网友评论