当前位置: 首页 > 知识库问答 >
问题:

为什么在Scala中独立代码块的执行时间依赖于执行顺序?[副本]

俞俊逸
2023-03-14

我有一个用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毫秒

有人能解释为什么吗?不应该都一样吗?哪个是真正的执行时间?

非常感谢,彼得

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

共有1个答案

佟和平
2023-03-14

这是Java的JIT在起作用。最初执行的是普通字节码,但过了一段时间(Oracle JVM默认情况下是1.5K/10K调用,请参见-xx:compilethreshold)后,优化开始处理实际执行的本机代码,这通常会导致性能的显著提高。

正如Ivan所提到的,还有中间字节码/本机代码的缓存和涉及到的各种其他技术,其中最重要的技术之一是垃圾收集器本身,它会给单个结果带来更多的差异。根据代码分配新对象的程度,这可能会在GC发生时完全破坏性能,但这是一个单独的问题。

为了在微基准测试时去除这些异常结果,建议您对操作的多个迭代进行基准测试,并丢弃底部和顶部5..10%的结果,然后根据剩余的样本进行性能评估。

 类似资料:
  • 以下代码的并行执行比顺序代码所需的时间长。我知道并行流比顺序流有更多的复杂性和更高的成本,我们不能期望并行流一直都能创造奇迹。我只是关心下面的代码 输出: > 具有顺序流:40 227 795 并行流:74 656 768 这个流有状态吗?如果不是,那么为什么平行流要花更长的时间呢?这背后的原因可能是什么?对此能有一个精确的猜测吗?

  • 我实现了以下promise函数,比如 cart.getbasket(req) Cart.UpdateBasket(req) Cart.UpdateDefaultShipport(req) cart.GetBasketObject(basket) 当前我执行代码时使用 我读过关于的文章,并想把它用作逻辑的流程,但当我使用时,它并不像预期的那样工作,因为我看到each的

  • 本文向大家介绍以下代码的执行顺序?相关面试题,主要包含被问及以下代码的执行顺序?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: //输出2,6,5,3,4,1 为什么呢?具体请参考我的文章: 从promise、process.nextTick、setTimeout出发,谈谈Event Loop中的Job queue

  • 问题内容: 我想测量在Python程序中评估一块代码所花费的时间,可能在用户cpu时间,系统cpu时间和经过时间之间进行区分。 我知道该模块,但是我有许多自写函数,在设置过程中传递它们并不是很容易。 我宁愿有一些可以使用的东西: 用户和系统的CPU时间不是必需的(尽管我想测量它们),但是对于过去的时间,我希望能够执行类似的操作,而不是使用复杂的命令或模块。 问题答案: 要获取以秒为单位的经过时间,

  • 问题内容: 我想知道以下代码的行为背后的机制是什么: 我的理解是不 返回 函数,而是 关闭连接/结束请求 。这可以解释为什么我仍然可以在命令后执行代码(我查看了快速源,但它似乎不是异步函数)。 还有其他我可能会想念的东西吗? 问题答案: 当然可以结束HTTP响应,但是它对您的代码没有做任何特殊的事情。 即使您已结束回复,也可以继续做其他事情。 但是,您 无法 做的是利用进行任何有用的操作。由于响应

  • Hprose 中间件的顺序执行是按照添加的前后顺序执行的,假设添加的中间件处理器分别为:handler1, handler2 … handlerN,那幺执行顺序就是 handler1, handler2 … handlerN。 不同类型的 Hprose 中间件和 Hprose 其它过程的执行流程如下图所示: +--------------------------------------------