我可以在没有输入的情况下使用ScalaMeter吗?

时间:2018-03-13 19:17:17

标签: scala scalameter

我想在Scala应用程序中对几种方法的运行时进行基准测试,我正在研究使用ScalaMeter。假设我想测量一个名为doSomething()的方法的时间。

我只想调用doSomething并测量运行一次所需的时间。但是,我在ScalaMeter中看到的所有文档都需要提供某种输入,无论是一系列整数,字符串还是其他东西。

是否可以使用ScalaMeter来完成我的要求?这是一个合适的用例吗?

1 个答案:

答案 0 :(得分:1)

这是可能的,但这会浪费时间。

您可能已经意识到, ScalaMeter 旨在消除功能执行时间变化的影响,以便可以准确地对这些执行时间进行基准测试。例如,您可能希望验证函数是否在所需时间内完成,或者确定在对代码库进行更改时是否保持其性能。

为什么这么具有挑战性?那么,需要克服许多障碍:

  1. JVM 有许多不同的选项可用于在程序中执行生成的 Java字节码。一些(例如 Zero VM )只解释代码;其他人利用即时 JIT )编译来优化转换到主机CPU的机器代码;随着时间的推移, HotSpot Server VM 会积极地提高性能,因此代码性能会逐渐提高运行时间。出于基准测试目的, HotSpot客户端虚拟机执行非常好的优化并快速达到稳定状态,因此我们可以快速开始测量性能。但是,我们仍然需要允许 JIT 编译器预热,因此我们必须忽略前几个较慢的执行(运行)否则会使我们的结果产生偏差。 ScalaMeter 可以自行完成 warmup ,但是要丢弃的运行次数是可配置的。
  2. JVM 执行许多垃圾收集 GC )周期,看似随机,这可能会在发生时同样降低性能。可以将 ScalaMeter 配置为忽略 GC 周期发生的执行。
  3. 主机的负载可能会因为在同一台机器上运行的其他进程执行线程而有所不同。这些也可能减慢执行时间。 ScalaMeter 通过仅考虑固定次数的最快观察时间来处理此问题,而不是采用平均值。
  4. 如果您从 SBT 运行,分叉JVM 执行会话将比共享相同 JVM <的分叉JVM 执行会话执行得更好,且变化更少/ em> instance as SBT (因为更多 SBT JVM 的资源将被使用)。
  5. 虚拟内存页面错误(其中组成应用程序的工作集的内存与/ em>页面文件交换)也将随机影响表现。
  6. 许多函数的性能取决于它的参数(如果你不是函数式编程共享的共同状态)。通过使用生成器 ScalaMeter 擅长将性能与参数值联系起来。 (例如,考虑size上的List操作 - 随着List中元素数量的增加,执行时间显然会更长。)
  7. 等。您可以在 ScalaMeter Getting Started Introduction
  8. 中找到有关这些问题的更多信息

    显然,基准测试应该在同一台主机上执行,以便结果具有可比性,因为CPU,操作系统,内存,BIOS配置等都会影响性能。

    因此,在解释了所有这些之后,您将理解为什么 ScalaMeter 需要执行相同的函数很多!; - )

    在您的情况下,doSomething()不带参数,因此您可以使用Gen[T].single生成器来标识doSomething()所属的类或对象,其类似于以下内容:

    注意:这是作为 ScalaMeter 测试编写的,因此源代码应位于src/test/scala下:

    import org.scalameter.api._
    import org.scalameter.picklers.Implicits._
    
    object MyBenchmark
    extends Bench.ForkedTime {
    
      // We have no arguments. Instead, create a single "generator" that identifies the class or
      // object that doSomething belongs to. This assumes doSomething() belongs to object
      // MyObject.
      val owner = Gen.single("owner")(MyObject) 
    
      // Measure MyObject.doSomething()'s performance.
      performance of "MyObject" in {
        measure method "doSomething()" in {
          using(owner) in {
            _.doSomething()
          }
        }
      }
    }
    

    (顺便说一句:我原本认为没有参数的基准测试功能会比这更简单,但这是迄今为止我能够提出的最佳功能。如果有人有更好的想法,请添加一个评论并告诉我!)

    所以,如果所有这些都是矫枉过正的话,你可能想尝试这样的事情:

    // Measure nanoseconds taken to execute by name argument.
    def measureTime(x: => Unit): Long = {
      val start = System.nanoTime()
      x
      // Calculate how long that took and return the value.
      System.nanoTime() - start
    }
    
    measureTime {
      doSomething()
    }
    

    你只会执行一次这个功能,每次花费的时间会大不相同。