如何用console.time做微基准测试以衡量编译器优化细微差异?
==与===的性能基准测试探讨 咱们来聊聊JavaScript里==和===的性能对比测试,这段测试代码是从其他示例改编来的,虽然写得不够简洁,但核心就是想搞明白基准测试到底怎么玩,同时还带出了几个值得琢磨的问题:
console.time能否准确测量真实执行时间?在各浏览器中的可靠性如何?- 是否更适合用
Date.now()或外部时间参考? - 如何检测编译器优化(比如忽略空循环)?
这次测试的核心目标是看看同类型比较时,==与===的性能有没有差异——理论上二者应该差不多,但实际在Firefox、Chrome和Node.js里跑出来的结果却不一样,这就引出了新的疑问:如果不同编译器的优化方向完全相反,取多个浏览器的平均时间到底合理吗?还有哪些更靠谱的估算执行时间的方法?
测试代码
var testString = "444442"; var testNumber = 444442; var testString2 = "444443"; var testObject = {}; var testObject2 = {x:1}; const MAX = 1000000; var name; var result; name='==='; result = 0; console.time('Operation '+name); for(var i = 0; i < MAX; i++){ result += Number( (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) && (testString2 === testString || testNumber === testNumber) && (testObject2 === testObject || testString === testString) && (result === testNumber || testObject === testObject) ) testNumber++ } console.timeEnd('Operation '+name); var result0 = result; name=='==' result = 0; console.time('Operation '+name); var result = null; for(var i = 0; i < MAX; i++){ result += Number( (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) && (testString2 == testString || testNumber == testNumber) && (testObject2 == testObject || testString == testString) && (result == testNumber || testObject == testObject) ) } console.timeEnd('Operation '+name); if (result0 != result) console.log("OOPS BUG, result0 differing result: ", result0, result) name='NoOp' result = 0; console.time('Operation '+name); for(var i = 0; i < MAX; i++){ result = true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true } console.timeEnd('Operation '+name);
测试结果
本文测试结果(省略毫秒后零值)
- Firefox环境:
Operation ===: 80 ms
Operation ==: 200 ms
Operation NoOp: 8 ms - Chrome环境:
Operation ===: 21 ms
Operation ==: 27 ms
Operation NoOp: 22 ms - Node.js环境:
Operation ===: 108 ms
Operation ==: 152 ms
Operation NoOp: 9 ms
本次测试结论:所有环境下===都略快于==。
Rick测试结果
- Firefox环境:
Operation ===: 1089 u
Operation ==: 280 u
Operation NoOp: 281 u - Chrome环境:
Operation ===: 352 u
Operation ==: 349 u
Operation NoOp: 349 u - Node.js环境:
Operation ===: 201 u
Operation ==: 387 u
Operation NoOp: 195 u
Rick测试结论:浏览器环境中==更快,Node.js里则是===更快。
最终结论
哪怕增加循环次数,基准测试的结果也没法做到统一。Rick测试很容易受到编译器优化的影响,而本文的测试相对更稳定。
注:理论上同类型比较时
==和===的性能是一致的,本来就很难测出差异,本文重点探讨的是如何用console.time来观测这类细微的性能差异。
内容的提问来源于stack exchange,提问作者Peter Krauss

