【问题标题】:Why do the execution time of independent code blocks depend on execution order in Scala? [duplicate]为什么独立代码块的执行时间取决于Scala中的执行顺序? [复制]
【发布时间】:2016-08-31 04:02:39
【问题描述】:

我有一个用 Scala 编写的程序。我想测量不同独立代码块的执行时间。当我以明显的方式执行此操作时(即在每个块之前和之后插入System.nanoTime()),我观察到执行时间取决于块的顺序。最初的一些块总是比其他块花费更多的时间。

我创建了一个重现此行为的简约示例。为简单起见,所有代码块都相同,并为整数数组调用 hashCode()

package experiments

import scala.util.Random

/**
  * Measuring execution time of a code block
  *
  * Minimalistic example
  */
object CodeBlockMeasurement {

  def main(args: Array[String]): Unit = {
    val numRecords = args(0).toInt
    // number of independent measurements
    val iterations = args(1).toInt

    // Changes results a little bit, but not too much
    // val records2 = Array.fill[Int](1)(0)
    // records2.foreach(x => {})

    for (_ <- 1 to iterations) {
      measure(numRecords)
    }
  }

  def measure(numRecords: Int): Unit = {
    // using a new array every time
    val records = Array.fill[Int](numRecords)(new Random().nextInt())
    // block of code to be measured
    def doSomething(): Unit = {
      records.foreach(k => k.hashCode())
    }
    // measure execution time of the code-block
    elapsedTime(doSomething(), "HashCodeExperiment")
  }

  def elapsedTime(block: => Unit, name: String): Unit = {
    val t0 = System.nanoTime()
    val result = block
    val t1 = System.nanoTime()
    // print out elapsed time in milliseconds
    println(s"$name took ${(t1 - t0).toDouble / 1000000} ms")
  }
}

使用numRecords = 100000iterations = 10 运行程序后,我的控制台如下所示:

HashCodeExperiment 耗时 14.630283 毫秒
HashCodeExperiment 耗时 7.125693 毫秒
HashCodeExperiment 耗时 0.368151 毫秒
HashCodeExperiment 耗时 0.431628 毫秒
HashCodeExperiment 耗时 0.086455 毫秒
HashCodeExperiment 耗时 0.056458 毫秒
HashCodeExperiment 耗时 0.055138 毫秒
HashCodeExperiment 耗时 0.062997 毫秒
HashCodeExperiment 耗时 0.063736 毫秒
HashCodeExperiment 耗时 0.056682 毫秒

有人可以解释这是为什么吗?不应该都一样吗?真正的执行时间是多少?

非常感谢,
彼得

环境参数:
操作系统:ubuntu 14.04 LTS(64 位)
IDE:IntelliJ IDEA 2016.1.1 (IU-145.597)
Scala:2.11.7

【问题讨论】:

  • 可靠地对 GCed 语言进行基准测试并不容易 - 对 GCed 和 JITed 语言进行基准测试更加困难。我想说你在前几次迭代中看到的是 JIT 和 JVM 运行时在工作。

标签: scala jvm measurement


【解决方案1】:

这是 Java 的 JIT 启动。最初执行纯字节码,但经过一段时间后(Oracle JVM 默认调用 1.5k/10k,请参阅 -XX:CompileThreshold)优化开始处理实际执行的本机代码通常会带来相当大的性能提升。

正如 Ivan 所提到的,还有中间字节码/本机代码的缓存以及所涉及的各种其他技术,其中最重要的技术之一是垃圾收集器本身,它会对单个结果造成更大的差异。取决于代码分配新对象的程度,这可能会在发生 GC 时绝对降低性能,但这是一个单独的问题。

要在进行微基准测试时去除此类异常结果,建议您对动作的多次迭代进行基准测试并丢弃底部和顶部 5..10% 的结果,并根据剩余样本进行性能估计。

【讨论】:

    【解决方案2】:

    简答:缓存。

    这些是独立的代码块,但运行不能完全独立,因为它们运行在同一个 JVM 实例中,并且在同一个 CPU 的同一个进程中。 JVM本身内部有很多优化,包括缓存。现代CPU也这样做。因此,这是很常见的行为,重新运行通常比第一次运行花费更少的时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-05
      • 1970-01-01
      • 2019-03-25
      • 2015-11-01
      • 1970-01-01
      • 2011-10-10
      • 2012-04-10
      • 2016-02-12
      相关资源
      最近更新 更多