|
如何计算JavaScript函数的性能时间:2020-11-23 要比较两个函数哪个性能更好,一个直观且公平的方法就是计算两个函数分别执行完的时间。 良好的性能更容易好的用户体验,而好的用户体验更能留住用户。 研究表明,由于性能问题,在 这也是为什么要提高性能的一个重要原因。 特别是使用 JS 开发时,编写的每一行 JS 都可能会阻塞DOM,因为它是单线程语言。 本次分享,我们主要介绍如何计算函数的性能。 Performance.now
根据 MDN : 这个时间戳实际上并不是高精度的。为了降低像Spectre这样的安全威胁,各类浏览器对该类型的值做了不同程度上的四舍五入处理。(Firefox从Firefox 59开始四舍五入到2毫秒精度)一些浏览器还可能对这个值作稍微的随机化处理。这个值的精度在未来的版本中可能会再次改善;浏览器开发者还在调查这些时间测定攻击和如何更好的缓解这些攻击。 因为,要计算一个函数的执行时间,分别比较函数执行前和执行后的两次 const t0 = performance.now(); for (let i = 0; i < array.length; i++) { // some code } const t1 = performance.now(); console.log(t1 - t0, 'milliseconds'); 在这里,我们可以看到 Firefox 中的结果与 Chrome 完全不同。 这是因为从版本 performance API 不当当只有返回时间戳这个功能,还有很多实用方法,大家可以根据需要到 MDN 查询相关的文档。 然而,对于我们的用例,我们只想计算单个函数的性能,因此时间戳就足够了。 performance.now() 和 Date.now一样吗? 你可能会想,嘿,我也可以使用 是的,你可以,但这有缺点。
基于系统时间的日期可能不太会被采用,对于实际的用户监视也不是理想的选择。 大多数系统运行一个守护程序,该守护程序定期同步时间。 通常每15至20分钟将时钟调整几毫秒。 以该速率,大约10秒间隔的1%将是不准确的。 Performance.mark 和 Performance.measure除了 Performance.mark先来看看MDN中关于mark方法的定义: The mark() method creates a timestamp in the browser's performance entry buffer with the given name. 这段话可以分解出三个关键词。首先 performance entry buffer指的是存储 最后就是given name,表示生成的每一个 所以这句话就可以理解成,在浏览器的performance entry buffer中,根据名称生成高精度时间戳。也就是很多人说过的“打点”。 就像 performance.mark('name'); 标记 的 performance entry将具有以下属性值:
Performance.measure同样先来看看 MDN 上关于 measure 的定义: 这段定义和上面 performance.measure(name, startMark, endMark); 计算两个mark之间的时长,创建一个
从导航开始测量 performance.measure('measure name'); 导航开始到标记 performance.measure('measure name', undefined, 'mark-2'); 从标记到标记 performance.measure('measure name', 'mark-1', 'mark-2'); 资源性能数据从 performance entry buffer 获取数据在上面的函数中,总是提到结果存储在 performance API有3个函数可以用来访问该数据: performance.getEntries() 获取一组当前页面已经加载的资源PerformanceEntry对象。接收一个可选的参数 let entries = window.performance.getEntries(); performance.getEntriesByName 根据参数 let entries = window.performance.getEntriesByName(name, type); performance.getEntriesByType 根据参数 var entries = window.performance.getEntriesByType(type); 结合事例: performance.mark('mark-1'); // some code performance.mark('mark-2') performance.measure('test', 'mark-1', 'mark-2') console.log(performance.getEntriesByName('test')[0].duration); Console.time这个 API确实易于使用。当需要统计一段代码的执行时间时,可以使用 console.time('test'); for (let i = 0; i < array.length; i++) { // some code } console.timeEnd('test'); 输出的结果与Performance API非常相似。
减少时间精度如果在不同的浏览器中使用上面提到的 api 测量函数,你可能会注意到结果是不同的。 这是由于浏览器试图保护用户免受时序攻击(timing attack)和指纹采集(Fingerprinting ),如果时间戳过于准确,黑客可以使用它们来识别用户。 例如,Firefox等浏览器试图通过将精度降低到2ms(版本60)来防止这种情况发生。 注意事项现在,我们已经知道了要测量JavaScript函数的速度所需方法。 但是,最好还要避免一些陷阱: 分而治之开发过程中,我们可能会我发现有些模块执行速度很慢,但是我们不知道具体问题出在哪里。解决一个方法是,使用上面提到的这些函数来测量它,而不是胡乱猜测代码的哪一部分比较慢。 要对其进行跟踪,首先将 注意输入值在实际应用中,给定函数的输入值可能会发生很大变化。 仅针对任意随机值测量函数的速度并不能提供我们可以实际使用的任何有价值的数据。 确保使用相同的输入值运行代码。 多次运行该函数假设你有一个函数,它的功是遍历一个数组,对数组的每个值进行一些计算,然后返回一个带有结果的数组。你想知道是 function testForEach(x) { console.time('test-forEach'); const res = []; x.forEach((value, index) => { res.push(value / 1.2 * 0.1); }); console.timeEnd('test-forEach') return res; } function testFor(x) { console.time('test-for'); const res = []; for (let i = 0; i < x.length; i ++) { res.push(x[i] / 1.2 * 0.1); } console.timeEnd('test-for') return res; } 然后这样测试它们: const x = new Array(100000).fill(Math.random()); testForEach(x); testFor(x); 如果在 Firefox 中运行上述函数,结果: 看起来 那如果是相同的输入,运行两次呢: testForEach(x); testForEach(x); testFor(x); testFor(x); 如果我们第二次调用 在多个浏览器中测试如果我们在Chrome中运行上述代码,结果又会不一样: 这是因为Chrome和Firefox具有不同的JavaScript引擎,它们具有不同类型的性能优化。 在本例中,Firefox 在对相同输入的
这是为什么要在多个引擎中进行测量的一个很好的例子。 如果仅使用 限制的 CPU我们在本地测试值是不能代表用户在浏览器使用的情况,因为 我们开发的电脑一般都会比大部分的用户好很多。 浏览器有一个特性可以限制CPU性能,我们通过设置可以更贴切一些真实情况。 总结在本文中,我们看到了一些JavaScript API,我们可以使用它们来衡量性能,以及如何在真实的项目中使用它们。 对于简单的测量,我发现使用
更多web前端开发知识,请查阅ots网络安全门户 !!! |