我正在修复 kotlinx.benchmark 中的一个错误,它包装了 JMH。开发速度要慢得多,因为测试实际上运行了基准测试,这对于我正在做的事情来说并不重要。我只需要验证 JMH 是否加载选项并正确选择基准测试,但实际上我并不需要 JMH 来运行和生成基准测试。
理想的方法是强制 JMH 在空运行模式下运行,即加载所有基准测试,但实际上并不运行它们。对于每个基准测试,它可能会返回假结果,甚至“0ms”结果。这样的事情可能吗?
以下是 kotlinx.benchmark 目前运行 JMH 的方式。
kotlinx.benchmark 通过 JvmBenchmarkRunner.kt
.中的
org.openjdk.jmh.runner.Runner
类调用 JMH。
JvmBenchmarkRunner
可能比看起来更复杂,但其核心只是构建 JMH Options,然后运行它们,如下所示:
import org.openjdk.jmh.annotations.Mode
import org.openjdk.jmh.results.format.ResultFormatType
import org.openjdk.jmh.runner.Runner
import org.openjdk.jmh.runner.options.OptionsBuilder
fun main() {
val options = OptionsBuilder()
.warmupForks(3)
.forks(2)
.mode(Mode.Throughput)
.resultFormat(ResultFormatType.TEXT)
// (more options)
.build()
val runner = Runner(options)
runner.run()
}
我使用 Kotlin/JVM 进行编码,但如果在 Java 中使用 JMH,这种情况也适用。
虽然可以将时间设置得非常低,例如1ms,以便测试(几乎)立即运行,这是不希望的,因为它会阻止测试是否将正确的参数传递给 JMH。
要在加载基准但不实际执行的空运行模式下运行 JMH,您可以通过自定义
Benchmark
实现来自定义 JMH 的行为来实现这一点。虽然 JMH 不提供开箱即用的内置试运行模式,但您可以使用以下方法解决此限制:
方法:自定义基准运行程序
创建自定义跑步者: 您可以创建 org.openjdk.jmh.runner.Runner 的自定义实现来覆盖运行基准测试的行为。这个自定义运行器将简单地模拟执行过程,而不是执行基准测试。
覆盖基准执行: 您需要重写负责运行基准测试的方法,以便它跳过实际执行但仍然处理基准测试选项。
以下是如何为此目的设置自定义运行器的示例:
自定义运行器实现:
import org.openjdk.jmh.runner.Runner
import org.openjdk.jmh.runner.options.Options
import org.openjdk.jmh.runner.options.OptionsBuilder
import org.openjdk.jmh.runner.options.TimeValue
import org.openjdk.jmh.results.format.ResultFormatType
import org.openjdk.jmh.results.format.ResultFormat
import org.openjdk.jmh.runner.BenchmarkClassLoader
import org.openjdk.jmh.runner.BenchmarkList
import org.openjdk.jmh.runner.options.TimeValue
import java.util.concurrent.TimeUnit
class DryRunRunner(options: Options) : Runner(options) {
override fun run() {
println("Dry-run mode: Benchmarks loaded but not executed.")
// Simulate loading benchmarks and printing them
val benchmarks = BenchmarkList(options.benchmarks)
benchmarks.forEach {
println("Benchmark: ${it.name}")
// You could generate a fake result or simply skip actual execution
// Here we simulate by printing a 0ms result
println("Result: 0ms")
}
}
}
使用自定义运行器 您可以使用此自定义运行程序来代替代码中的标准运行程序:
fun main() {
val options = OptionsBuilder()
.warmupForks(3)
.forks(2)
.mode(Mode.Throughput)
.resultFormat(ResultFormatType.TEXT)
.build()
val runner = DryRunRunner(options)
runner.run()
}
注释
BenchmarkList:您需要创建或找到合适的类或方法来列出 DryRunRunner 中的基准。 JMH 不会直接公开这一点,因此您可能需要使用反射或其他方法,具体取决于内部 JMH API。
结果模拟:在 DryRunRunner 中,您可以通过打印 0ms 来模拟结果。您可以自定义它以满足您的需求。
集成测试:如果您还对 JMH 如何测试自身感兴趣,JMH 确实使用一些内部机制进行自己的测试,但它通常照常运行基准测试。您可能找不到用于跳过执行的直接内部测试模式。
通过实施上述方法,您应该能够验证 JMH 是否已正确配置,而无需实际运行基准测试。