迁移到Scala3后Range基准测试性能大幅下降相关问题咨询
问题描述
我已知晓Scala2中Range相比普通循环性能更慢的情况,将对应基准测试迁移到Scala3后,观察到Range类的性能出现了极为严重的下滑。
注意while循环的耗时在两个版本中表现相近。
我想咨询以下两个问题:
- 我的基准测试设计是否正确?
- Scala3是否修改了Range的实现,才导致了可预期的性能下降?
性能测试结果
Scala2: while loop time: 0.015882699999999996 ms range loop time: 0.20659249999999996 ms Scala3: while loop time: 0.011921900000000001 ms range loop time: 464.97765689999994 ms
基准测试代码
package scalashop import org.scalameter.{Key, Warmer, config} object TestDoubleLoopPerformance { val standardConfig = config( Key.exec.minWarmupRuns := 5, Key.exec.maxWarmupRuns := 10, Key.exec.benchRuns := 10, // Key.verbose := true ) withWarmer(Warmer.Default()) def main(args: Array[String]): Unit = { val whileLoopTime = standardConfig measure { for (iter <- 0 to 100) { sumWithWhileLoop(1000, 2000) } } println(s"while loop time: $whileLoopTime") val rangeLoopTime = standardConfig measure { for (iter <- 0 to 100) { sumWithRange(1000, 2000) } } println(s"range loop time: $rangeLoopTime") } private def sumWithRange(xEnd: Int, yEnd: Int) = { var sum = 0 for { x <- 0 until xEnd y <- 0 until yEnd } sum += 1 sum } private def sumWithWhileLoop(xEnd: Int, yEnd: Int) = { var sum = 0 var x = 0 while (x < xEnd) { var y = 0 while (y < yEnd) { sum += 1 y += 1 } x += 1 } sum } }
测试环境
- JVM: openjdk 11.0.9.1
- Scalameter: 0.21
解答
- 基准测试设计整体合理。你已经配置了预热次数,排除了JIT未生效的干扰,while循环在两个Scala版本下耗时几乎一致,也侧面验证了测试框架的基准稳定性。如果要进一步提升结果可信度,可以开启Scalameter的verbose模式,确认预热次数足够,避免局部JIT优化的干扰。
- 该性能下降是Scala3的Range实现调整导致的可预期结果。Scala3取消了Scala2中针对Range for循环的专用编译器优化,嵌套Range的for推导式不会被自动转换为等价的while循环,而是会生成完整的集合迭代器实例,每层迭代都会产生额外的对象创建、方法调用开销,在嵌套场景下开销被指数级放大,最终出现上千倍的性能差距。如果要在Scala3中获得接近while循环的性能,可直接使用while循环,或者手动使用优化后的迭代API实现逻辑。
内容的提问来源于stack exchange,提问作者Lesiak
相关产品推荐
相关产品推荐

