是否可以使用 JMH 作为 Maven 验证检查?
Is it possible to use JMH as a maven validation check?
我有一段性能关键代码,我想将其作为 Maven 构建步骤进行保护,即 JMH 会 运行 并检查性能是否因本地更改而降低。
如何使用 JMH 检查此类退化?
我找到了一些相关链接:
- Perf Testing in CI
- C++ CI with Perf Metrics
- Chapter: Perf in CI
- CI with JUnit and ContiPerf(使用
@Required()
注释)
我之前实现了自动化性能测试(尽管不是在 Java 中,也不是在 CI 环境中)。需要注意的一个关键点是,您永远不会 运行 将其作为绝对值,因为基准测试所使用的机器 运行 可能会有所不同。 BogoMips 或测试相关类型的参考可用作相对比较。然后将基准测量为该参考时间的某个倍数,具有上限和下限。
虽然您通常会担心基准测试变慢(降级),但检查上限也很重要,因为它可能表明意外加速(更好的硬件支持),这应该表明 per-system/architecture绑定需要检查
JMH Maven 插件不支持此功能。您必须编写自己的 Maven 插件来执行此操作,或者您必须在构建生命周期内使用 java exec 插件来执行测试。您可以将基准测试结果写入文件并找到另一个 Maven 插件,该插件读取文件并在不匹配给定约束时中断构建。
但是,我怀疑这是个好主意。如果您的代码更改显着改变了基准,则很可能您的基准不再适合您的代码。更糟糕的是,即使您的代码变慢了,您的基准测试也可能会变得更快,因为基准测试不再反映真实的用例。此外,您必须找到基准,因为基准不适合衡量 "absolute" 运行时间。
对于这种方法适用的地方可能存在一些特殊情况,但您应该考虑是否真的值得麻烦。
看起来有可能,这个 netty.io commit 添加了对包装在 Junit 测试中的 JMH 的支持。
我从 JMH 选项中注意到:
-rf <type> Result format type.
See the list of available result formats first.
-rff <filename> Write results to given file.
意味着我可以告诉 Benchmark 将其结果输出到一个 JSON 文件,然后我可以将其作为 Junit 运行.
的一部分进行解析
最后一部分是比较 运行 Junit 内部的其他东西,也许 SPECjvm2008 ?
我建议通过 OptionsBuilder
简单地构建一组 Runner
选项,并从 Junit 测试中对其调用 run
。
虽然一些作者以不在 "clean" 环境中 运行 为基准而反对这样做,但我认为效果非常微不足道,并且在与参考 运行 进行比较时可能无关紧要同样的环境。
有关手动设置 Runner
的最简单示例,请参阅 here。
Runner.run()
(或在单个基准测试的情况下 Runner.runSingle()
)然后将 return 一个 Collection<RunResult>
或只是 RunResult
,断言可以是反对。
为了做到这一点,您可以简单地使用 Statistics
(参见文档 here),您可以通过 RunResult.getPrimaryResult().getStatistics()
从 RunResult
中提取并针对数字进行断言您可以从 Statistics
中提取的值
... 或使用 isDifferent()
方法,您可以选择在置信区间内比较两个基准 运行s(可能有助于自动捕获两个方向的异常值)。
我有一段性能关键代码,我想将其作为 Maven 构建步骤进行保护,即 JMH 会 运行 并检查性能是否因本地更改而降低。
如何使用 JMH 检查此类退化?
我找到了一些相关链接:
- Perf Testing in CI
- C++ CI with Perf Metrics
- Chapter: Perf in CI
- CI with JUnit and ContiPerf(使用
@Required()
注释)
我之前实现了自动化性能测试(尽管不是在 Java 中,也不是在 CI 环境中)。需要注意的一个关键点是,您永远不会 运行 将其作为绝对值,因为基准测试所使用的机器 运行 可能会有所不同。 BogoMips 或测试相关类型的参考可用作相对比较。然后将基准测量为该参考时间的某个倍数,具有上限和下限。
虽然您通常会担心基准测试变慢(降级),但检查上限也很重要,因为它可能表明意外加速(更好的硬件支持),这应该表明 per-system/architecture绑定需要检查
JMH Maven 插件不支持此功能。您必须编写自己的 Maven 插件来执行此操作,或者您必须在构建生命周期内使用 java exec 插件来执行测试。您可以将基准测试结果写入文件并找到另一个 Maven 插件,该插件读取文件并在不匹配给定约束时中断构建。
但是,我怀疑这是个好主意。如果您的代码更改显着改变了基准,则很可能您的基准不再适合您的代码。更糟糕的是,即使您的代码变慢了,您的基准测试也可能会变得更快,因为基准测试不再反映真实的用例。此外,您必须找到基准,因为基准不适合衡量 "absolute" 运行时间。
对于这种方法适用的地方可能存在一些特殊情况,但您应该考虑是否真的值得麻烦。
看起来有可能,这个 netty.io commit 添加了对包装在 Junit 测试中的 JMH 的支持。
我从 JMH 选项中注意到:
-rf <type> Result format type.
See the list of available result formats first.
-rff <filename> Write results to given file.
意味着我可以告诉 Benchmark 将其结果输出到一个 JSON 文件,然后我可以将其作为 Junit 运行.
的一部分进行解析最后一部分是比较 运行 Junit 内部的其他东西,也许 SPECjvm2008 ?
我建议通过 OptionsBuilder
简单地构建一组 Runner
选项,并从 Junit 测试中对其调用 run
。
虽然一些作者以不在 "clean" 环境中 运行 为基准而反对这样做,但我认为效果非常微不足道,并且在与参考 运行 进行比较时可能无关紧要同样的环境。
有关手动设置 Runner
的最简单示例,请参阅 here。
Runner.run()
(或在单个基准测试的情况下 Runner.runSingle()
)然后将 return 一个 Collection<RunResult>
或只是 RunResult
,断言可以是反对。
为了做到这一点,您可以简单地使用 Statistics
(参见文档 here),您可以通过 RunResult.getPrimaryResult().getStatistics()
从 RunResult
中提取并针对数字进行断言您可以从 Statistics
... 或使用 isDifferent()
方法,您可以选择在置信区间内比较两个基准 运行s(可能有助于自动捕获两个方向的异常值)。