Mic*_*jer 4 performance benchmarking kotlin
我注意到Itarable.sum()手动总和的性能和直接循环之间的惊人差异.考虑一下:
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()
})
}
}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是,Iterable.sum()与sum()实现几乎相同的代码相比,使用速度慢了10倍.这是为什么?
更新:
当我以js为目标时,sum()只会稍慢一些.
measureTimeMillis() 可以定义为:
import kotlin.js.Date
public inline fun measureTimeMillis(block: () -> Unit): Double {
val start = Date.now()
block()
return Date.now() - start
}
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
因此,在同一台机器上,jvm在使用时似乎比js慢sum().又一个惊喜.
显然,我们在这里比较超优化的紧密循环.对于"内置"案例中的"手动总和"和狂野差异,我看到了相当稳定的结果.这表明GC活动.
在启动VisualVM并使用其VisualGC插件时,我确认在手动求和计算期间没有GC活动,但在内置的情况下有很多.
查看生成的字节码,差异变得明显:成语for (i in 1..range) { ... }直接编译成计数循环.这实际上记录在案:
体型范围(
IntRange,LongRange,CharRange)有一个额外的功能:它们可以遍历.编译器负责将其类似地转换为Java的索引for循环,而无需额外的开销.
不幸的是,相同的优化不适用于扩展功能,Iterable.sum()因为它必须适用于任何扩展功能Iterable.编译器可以看到正在发生的事情并引入另一个内在函数,它只是简单地将整个事物转换为结果总和而不进行计算,或者如果范围界限不是硬编码则使用直接公式.
JavaScript在这里也有类似的基础,因为它也有一个强大的JIT编译器.我无法评论任何具体的内容,但它最有可能避免在热循环中进行分配.
| 归档时间: |
|
| 查看次数: |
127 次 |
| 最近记录: |