【问题标题】:Performance comparison of strict collection vs view严格集合与视图的性能比较
【发布时间】:2023-04-01 14:55:01
【问题描述】:

我正在对 Scala 中严格(急切执行评估)和非严格(延迟执行评估)的集合执行多个后续转换之间的性能比较。

def time[R](block: => R): R = {
    val t0 = System.nanoTime()
    val result = block // call-by-name
    val t1 = System.nanoTime()
    println("Elapsed time: " + (t1 - t0)/1e9 + "s")
    result
}

/* A view on a collection makes all transformations lazy, which makes it
   possible to combine multiple transformations into one. */

// The non-lazy (eager) version:
time {
    (1 to 1e7.toInt).map(_ + 2).map(x => {
        if(x % 2 != 0) -x else x
    }).sum
}

// The lazy version using a view:
time {
    (1 to 1e7.toInt).view.map(_ + 2).map(x => {
        if(x % 2 != 0) -x else x
    }).force.sum
}

在我的笔记本电脑上,第一次运行急切版本比懒惰版本慢。请参阅下面的时间安排。

渴望版本:2.4 秒

惰性版本:0.7 秒

但是,从第二次运行开始,它们都需要大约 0.7 秒。为什么?

运行时环境:

  • Scala 2.11.7
  • Java 1.8
  • OS X 10.10

【问题讨论】:

    标签: performance scala collections profiling lazy-evaluation


    【解决方案1】:

    跳过冷运行

    在分析代码时,称为冷运行的第一次运行/迭代通常不会被测量,因为它会为操作系统和/或运行时环境产生一些初始成本。

    对于在 JVM 上执行的 Java 和 Scala 代码,某些指令的第一次执行可能需要将类和方法加载并解析到内存中,从而产生一些开销。

    JVM 还将在第一次迭代期间用本地指令替换一些数学运算,例如,从而加快后续迭代的执行速度。

    【讨论】:

    • 那么你如何解释相同功能的惰性版本在冷运行和“热运行”中一样好?
    • @qed 初始开销将发生在第一次使用任何依赖的类或方法时,它与循环迭代的语法结构无关。初始成本可能在 Eager 版本的第一次执行期间应用,而 lazy 版本的第一次执行使用内存中加载的方法,因此速度更快。
    【解决方案2】:

    众所周知,获得准确的基准非常困难 - 如果上面的代码反映了您用于获得这些结果的实际代码,那么您不太可能获得可靠的数据。

    您应该使用真正的基准测试工具,例如https://github.com/ktoso/sbt-jmh

    一旦您确定性能存在显着差异,您就可以开始调查如何/为什么。

    【讨论】:

      猜你喜欢
      • 2014-04-24
      • 1970-01-01
      • 1970-01-01
      • 2022-01-07
      • 2012-02-22
      • 1970-01-01
      • 2019-01-28
      • 2013-07-20
      • 2017-09-21
      相关资源
      最近更新 更多