性能测试 基准测试
我们每个人每时每刻都在尝试评估代码某些部分的性能。 它可以是按每分钟特定数量的用户交易量衡量的功能齐全的应用程序,也可以是微基准测试。 您所写的证明您的开发人员是错误的。 特别是在后一种情况下,意识到基准测试结果歪曲的陷阱是有益的。 回顾七年前创建的基准,我不得不承认我已经在一些我实际上是错误的案例中证明了自己的观点。 抱歉,亲爱的朋友。 但是我在此过程中学到了一些东西,认为值得分享。 这样您就不会陷入同样的愚蠢状况。
陷阱1:嘈杂的邻居。
在自己的PC上运行基准测试时,您不会在其旁边启动DVD-RIP。 但是您在后台忘记的洪流又如何呢? 还是Windows拉下一个更新? 像这样的问题使我的测试结果出现了几次偏差。
因此,应该只在服务器上进行基准测试。 但是请注意,使用所有虚拟化平台,它可能变得更加棘手。 当您为测试驱动器启动下一个虚拟实例时,有可能与其他人共享相同的物理硬件。 现在,如果资源难以虚拟化(例如I / O),或者您遇到了弹性问题,例如CPU短暂的剧烈爆发,然后由系统管理程序强制进行了节流,那么您就会遇到问题。 至少在尝试达到基准测试结果时。
陷阱2:垃圾收集暂停。
衡量垃圾收集环境的性能还必须考虑垃圾收集的暂停时间。 因此–每当运行基准测试时,建议您通过-XX:+ PrintGCDetails打开垃圾收集器日志记录。 现在,当监视日志时:
0.415: [GC 0.415: [ParNew: 19136K->2112K(19136K), 0.0255607 secs] 100164K->100154K(118108K), 0.0256139 secs] [Times: user=0.04 sys=0.01, real=0.03 secs]
0.451: [GC 0.451: [ParNew: 19136K->19136K(19136K), 0.0000286 secs]0.451: [CMS0.461: [CMS-concurrent-mark: 0.033/0.099 secs] [Times: user=0.12 sys=0.04, real=0.10 secs]
0.580: [Full GC 0.580: [CMS: 107774K->107774K(107776K), 0.0553739 secs] 126910K->126910K(126912K), [CMS Perm : 4620K->4620K(21248K)], 0.0554400 secs] [Times: user=0.05 sys=0.00, real=0.05 secs]
0.635: [Full GC 0.635: [CMS: 107774K->107775K(107776K), 0.0919800 secs] 126910K->126872K(126912K), [CMS Perm : 4620K->4609K(21248K)], 0.0920382 secs] [Times: user=0.09 sys=0.00, real=0.09 secs]
您可以估算对基准的影响。 从上面的示例中可以看到,该程序仅运行635毫秒,在这段时间内,我们花费了270毫秒(42.5%)等待GC正常工作。 现在,我并不是在这里说您的测试应该配置为避免GC暂停。 我只是警告说,在进行测试的情况下,例如大型数据结构的行为,您应该注意测试在某些时候不会成为GC绑定。
陷阱3:及时编译。
从JVM在运行时优化代码的意义上讲,现代JVM令人惊叹。 因此,在大多数情况下,您不希望在JIT编译完成其职责之前就获得与性能相关的结论。 要知道基准测试期间JIT在做什么,您需要在JVM参数中添加-XX:+ PrintCompilation并监视日志:
240 org.codehaus.groovy.reflection.CachedMethod::compareToMethod (125 bytes)
241 java.util.zip.ZipFile::access$300 (5 bytes)
242 java.util.ArrayList::indexOf (67 bytes)
243 java.util.Arrays::fill (28 bytes)
72 made zombie sun.misc.URLClassPath::getLoader (136 bytes)
在大多数情况下,这里的技巧是等待JIT完成优化任务并从该点开始监视。 例如,从我们的上一篇博客文章中可以看出,结果的性能差异可能高达100倍 。
陷阱4:类加载器。
注意类加载的效果。 JVM是一个懒惰的混蛋,不会在真正需要它们之前加载类。 对于基准测试,这意味着您不应在类加载完成之前打开测量。 对于微基准测试,通常并不太难–您只需要进行预热阶段。 但是,在对现实世界中的事物进行基准测试时,您可能希望打开-verbose:class标志以检查是否在打开测量之前加载了所有类。
陷阱5:机器架构。
如果要对要在生产代码中使用的东西进行基准测试,则应在生产环境所使用的相同体系结构上运行测试。 否则,由于32/64位差异,GC算法差异,堆区域配置,内核数量等原因,您可能会获得完全偏差的结果。
陷阱6:测量。
首先是度量单位。 如果您的结果以纳秒为单位进行度量,则很可能您处于错误的领域。 尝试创建更粗糙的测试,以便至少可以进入毫秒级。 更好-在几十秒内。 测量很小的工作单元可能会再次扭曲结果–测量本身可能会太大地改变结果。 第二–测量几次。 实际上,如果可以的话,几十次。 我并不是说您要在代码中对基准进行30倍的迭代。 我的意思是您启动了整个基准测试30次并汇总了结果。
现在,如果这里的所有内容都使您不敢写微基准,那就好了。 因为大多数时候,您的微基准测试无法帮助您解决应用程序中的实际性能问题。 只需编写简单,清晰的代码即可避免明智的优化。 这是现在和将来的JIT可能会优化自己的代码。 那是真正应该属于他们的工作,而不是您的工作。 但是,如果除了害怕,您还感到好奇,那么–我们还有很多有趣的内容要分享。 只需在Twitter或RSS 上关注我们,即可及时收到通知。
参考: Plumbr Blog博客上的JCG合作伙伴 Ivo Magi进行基准测试时常见的陷阱 。
翻译自: https://www.javacodegeeks.com/2013/02/common-pitfalls-in-benchmarking.html
性能测试 基准测试