这篇文章的原文是英文,我觉得其中的方式简便而且是我们应该掌握的基本的性能测试的方式,所以对原文进行了简单的翻译
原文地址:Measuring the Performance of JavaScript Functions
衡量 JavaScript 函数的性能
衡量执行功能所花费的时间始终是一个好主意,以证明某些实现比其他实现更具性能。这也是确保性能在进行一些更改后不受影响并找出瓶颈的好方法。
良好的性能有助于获得良好的用户体验。良好的用户体验使用户回来。 例如,如 本研究所示,由于性能问题,在 88%的在线消费者因不良的用户体验而退货的可能性较小。
因此,重要的是能够识别代码中的瓶颈并衡量改进。特别是在为浏览器开发 JavaScript 时,重要的是要意识到,您编写的每一行 JavaScript 都可能会阻塞 DOM,因为它是单线程语言。
在本文中,我将解释如何测量函数的性能以及如何从函数中获得结果。
我在这里提到的函数非常适合在底层调试 JavaScript 函数。如果您想确保应用程序保持快速运行,即使添加了更多功能,也请考虑执行 性能预算。
Perfomance.now
Perfomance API 通过其函数提供对 DOMHighResTimeStamp 的访问,该函数 performance.now()返回自页面加载以来经过的时间(以毫秒为单位),精度最高为 5µs(以分数为单位)。
因此在实践中,您需要获取两个时间戳,将它们保存在变量中,然后用第二个时间戳减去第一个时间戳
const t0 = performance.now();
for (let i = 0; i < array.length; i++)
{
// some code
}
const t1 = performance.now();
console.log(t1 - t0, 'milliseconds');
Output (Chrome):
0.6350000001020817 "milliseconds"
Output (Firefox):
1 milliseconds
在这里,我们可以看到 Firefox 中的结果与 Chrome 完全不同。这是因为从版本 60 开始,Firefox 将性能 API 的精度降低到 2ms。
那不是和 Date.now 一样吗?
现在您可以想到,嘿,我也可以使用 Date.now。
是的,您可以,但是有缺点。
Date.now 返回自 Unix 时代(1970-01-01T00:00:00Z)以来经过的时间(以毫秒为单位),并取决于系统时钟。这不仅意味着它不够精确,而且还不总是递增。WebKit 工程师(Tony Gentilcore)的解释如下:
基于系统时间的日期可能很少被考虑,对于实际的用户监视也不是理想的选择。大多数系统运行一个守护程序,该守护程序定期同步时间。通常每 15 至 20 分钟将时钟调整几毫秒。以这种速度,大约 10 秒间隔的 1%将是不准确的。
Console.time
该 API 确实易于使用。只需 console.time 在 console.timeEnd 要测量的代码之前和之后放置,然后使用相同的 string 参数调用该函数。一页上最多可以同时使用 10,000 个计时器。
精度与 Perfomance API 相同,但这又取决于浏览器。
console.time('test');
for (let i = 0; i < array.length; i++) {
// some code
}
console.timeEnd('test');
Output (Chrome):
test: 0.766845703125ms
Output (Firefox):
test: 2ms - timer ended
console.time 优点是易于使用,因为它不需要手动计算两个时间戳之间的差异。
时间精度在降低
如果您在不同的浏览器中使用上述 API 来衡量功能,则可能会注意到结果有所不同。
这是由于浏览器试图保护用户免受定时攻击 和指纹侵害,如果时间戳过于准确,黑客可以使用它们来识别用户。
例如,像 Firefox 这样的浏览器试图通过将精度降低 到 2ms(版本 60)来防止这种情况。
注意
- 分而治之
您发现过滤某些结果时发现速度很慢,但您不知道瓶颈在哪里。
您可以使用上面提到的这些功能来度量代码,而不必大胆猜测代码的哪一部分慢。
要对其进行跟踪,首先将 console.time 语句放在缓慢的代码块周围。然后衡量他们不同部分的表现。如果一个比另一个慢,那就继续往下走,直到发现瓶颈为止。
这些语句之间的代码越少,则跟踪不感兴趣的内容的可能性就越小。
- 注意输入值
在实际应用中,给定函数的输入值可能会发生很大变化。仅针对任意随机值测量函数的速度并不能提供我们可以实际使用的任何有价值的数据。
确保使用相同的输入值运行代码。
- 多次运行功能
eg:
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 中运行上述功能,您将获得类似以下的输出:
test-forEach: 27ms - timer ended
test-for: 3ms - timer ended
看起来 forEach 变慢了,对吧?
让我们看看是否使用相同的输入两次运行相同的函数:
testForEach(x);
testForEach(x);
testFor(x);
testFor(x);
test-forEach: 13ms - timer ended
test-forEach: 2ms - timer ended
test-for: 1ms - timer ended
test-for: 3ms - timer ended
如果我们 forEach 第二次调用测试,它的性能与 for 循环一样好。forEach 考虑到初始值较慢,可能还是不值得使用。
- ...and in multiple browsers
如果我们在 Chrome 中运行上述代码,结果会突然看起来不同:
test-forEach: 6.156005859375ms
test-forEach: 8.01416015625ms
test-for: 4.371337890625ms
test-for: 4.31298828125ms
这是因为 Chrome 和 Firefox 具有不同的 JavaScript 引擎,并且具有不同类型的性能优化。意识到这些差异是一件好事。
在这种情况下,Firefox 在优化 forEach 相同输入的使用方面做得更好。
for 在两个引擎上的性能都更好,因此最好坚持 for 循环。
这是一个很好的示例,说明了为什么应该在多个引擎中进行测量。如果仅使用 Chrome 浏览器进行测量,则可能得出的结论 forEach 与相比还算不错 for。
- 限制您的 CPU
这些价值似乎并不多。请注意,您的开发机器通常比浏览您网站的普通手机快得多。
为了了解它的外观,浏览器具有一项功能,可让您限制 CPU 性能。
这样,这 10 或 50 毫秒很快就会变成 500 毫秒。
- 衡量相对表现
这些原始结果实际上不仅取决于您的硬件,还取决于您的 CPU 和 JavaScript 线程当前的负载。尝试着重于测量的相对改进,因为下次您重新启动计算机时,这些数字看起来可能会大不相同。
结论
在本文中,我们看到了一些 JavaScript API,我们可以使用它们来衡量性能,以及如何在“真实世界”中使用它们。对于简单的测量,我发现可以优先使用 console.time。