我可以在没有输入的情况下使用 ScalaMeter 吗?
Can I use ScalaMeter with no input?
我想对我的 Scala 应用程序中几种方法的 运行时间进行基准测试,我正在研究使用 ScalaMeter。假设我想测量一个名为 doSomething()
.
的方法的时间
我只想调用doSomething
并测量一次运行所花费的时间。但是,我看到的所有 ScalaMeter 文档都需要提供某种输入,无论是一系列整数、字符串还是其他内容。
是否可以使用 ScalaMeter 来完成我的要求?这是一个合适的用例吗?
可以,但会浪费时间。
您可能已经知道,ScalaMeter 旨在消除函数执行时间变化的影响,因此可以准确地对这些执行时间进行基准测试。例如,您可能想要验证函数是否在要求的时间内完成,或者确定其性能是否随着代码库的更改而保持不变。
为什么这么有挑战性?那么,有许多障碍需要克服:
- JVM 有许多不同的选项用于在程序中执行生成的 Java 字节码。有些(例如 Zero VM)只是解释代码;其他人利用 just-in-time (JIT) 编译来优化翻译到主机 CPU 的 机器代码; HotSpot Server VM 随着时间的推移积极提高性能,因此代码性能会随着运行时间的延长而逐步提高。出于基准测试目的,HotSpot Client VM 执行了非常好的优化并很快达到 steady-state,因此我们可以快速开始测量性能。但是,我们仍然需要让 JIT 编译器预热,因此我们必须忽略前几次较慢的执行 (runs)否则会使我们的结果产生偏差。 ScalaMeter 本身就很好地完成了这个 warmup,但是要丢弃的运行次数是可配置的。
- JVM 执行多个 垃圾收集 (GC) 周期,似乎在随机,当它们发生时同样会降低性能。 ScalaMeter 可以配置为忽略发生 GC 循环的执行。
- 主机的负载可能会有所不同,因为它在同一台机器上执行来自其他进程 运行 的线程。这些也可能会减慢执行时间。 ScalaMeter 通过仅考虑固定运行次数中观察到的最快时间来处理此问题,而不是取平均值。
- 如果您是来自 SBT 的 运行,forked JVM 执行会话将执行得更好,变化更少,而不是与 SBT 共享相同 JVM 实例的实例(因为更多 SBT JVM资源将被使用)。
- 虚拟内存页面错误(其中构成应用程序工作集的内存被切换to/from a 分页文件) 也会随机影响性能。
- 许多函数的性能将取决于它的参数(而且,如果您不喜欢 函数式编程,共享可变状态 ).通过使用 generators,将性能与参数值联系起来也是 ScalaMeter 擅长的事情。 (例如,考虑
List
上的 size
操作——随着 List
中元素数量的增加,执行时间显然会更长。)
- 等您可以在 ScalaMeter Getting Started Introduction.
中找到有关这些问题的更多信息
显然,应该在同一台主机上执行基准测试,这样结果才具有可比性,因为 CPU、OS、内存、BIOS 配置等都会影响性能也是。
所以,在解释了所有这些之后,您就会明白为什么 ScalaMeter 需要执行相同的功能 很多! ;-)
在您的情况下,doSomething()
不接受任何参数,因此您可以使用 Gen[T].single
生成器来标识 class 或 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()
}
你只会执行一次函数,每次所用的时间都会大不相同。
我想对我的 Scala 应用程序中几种方法的 运行时间进行基准测试,我正在研究使用 ScalaMeter。假设我想测量一个名为 doSomething()
.
我只想调用doSomething
并测量一次运行所花费的时间。但是,我看到的所有 ScalaMeter 文档都需要提供某种输入,无论是一系列整数、字符串还是其他内容。
是否可以使用 ScalaMeter 来完成我的要求?这是一个合适的用例吗?
可以,但会浪费时间。
您可能已经知道,ScalaMeter 旨在消除函数执行时间变化的影响,因此可以准确地对这些执行时间进行基准测试。例如,您可能想要验证函数是否在要求的时间内完成,或者确定其性能是否随着代码库的更改而保持不变。
为什么这么有挑战性?那么,有许多障碍需要克服:
- JVM 有许多不同的选项用于在程序中执行生成的 Java 字节码。有些(例如 Zero VM)只是解释代码;其他人利用 just-in-time (JIT) 编译来优化翻译到主机 CPU 的 机器代码; HotSpot Server VM 随着时间的推移积极提高性能,因此代码性能会随着运行时间的延长而逐步提高。出于基准测试目的,HotSpot Client VM 执行了非常好的优化并很快达到 steady-state,因此我们可以快速开始测量性能。但是,我们仍然需要让 JIT 编译器预热,因此我们必须忽略前几次较慢的执行 (runs)否则会使我们的结果产生偏差。 ScalaMeter 本身就很好地完成了这个 warmup,但是要丢弃的运行次数是可配置的。
- JVM 执行多个 垃圾收集 (GC) 周期,似乎在随机,当它们发生时同样会降低性能。 ScalaMeter 可以配置为忽略发生 GC 循环的执行。
- 主机的负载可能会有所不同,因为它在同一台机器上执行来自其他进程 运行 的线程。这些也可能会减慢执行时间。 ScalaMeter 通过仅考虑固定运行次数中观察到的最快时间来处理此问题,而不是取平均值。
- 如果您是来自 SBT 的 运行,forked JVM 执行会话将执行得更好,变化更少,而不是与 SBT 共享相同 JVM 实例的实例(因为更多 SBT JVM资源将被使用)。
- 虚拟内存页面错误(其中构成应用程序工作集的内存被切换to/from a 分页文件) 也会随机影响性能。
- 许多函数的性能将取决于它的参数(而且,如果您不喜欢 函数式编程,共享可变状态 ).通过使用 generators,将性能与参数值联系起来也是 ScalaMeter 擅长的事情。 (例如,考虑
List
上的size
操作——随着List
中元素数量的增加,执行时间显然会更长。) - 等您可以在 ScalaMeter Getting Started Introduction. 中找到有关这些问题的更多信息
显然,应该在同一台主机上执行基准测试,这样结果才具有可比性,因为 CPU、OS、内存、BIOS 配置等都会影响性能也是。
所以,在解释了所有这些之后,您就会明白为什么 ScalaMeter 需要执行相同的功能 很多! ;-)
在您的情况下,doSomething()
不接受任何参数,因此您可以使用 Gen[T].single
生成器来标识 class 或 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()
}
你只会执行一次函数,每次所用的时间都会大不相同。