怎样测试 JavaScript 的函数性能

更新日期: 2020-03-04阅读: 1.6k标签: 性能

通过衡量执行某个函数所花费的时间,以“证明”某些实现比另一些实现更高效始终是一个很好的主意。这也是确保性能在进行一些修改后不受影响并找出瓶颈的好方法。

良好的性能有助于获得良好的用户体验。良好的用户体验能够留住用户。像此研究所示,由于性能差的用户体验,88% 的在线消费者回头的可能性较小问题。

所以重要的是能够识别代码中的瓶颈并进行评估改进。特别是在为浏览器开发 JavaScript 时,重要的是应该意识到,你编写的每一行 JavaScript 都可能会阻塞 dom,因为它是单线程语言。

在本文中,我将解释如何测量函数的性能,以及如何从函数中获得结果。

如果你发现某些计算过于繁琐而无法在主线程上进行计算,则你甚至可以考虑将其放入服务或 Web Worker 中。我在这篇文章中解释了如何传递和接收来自 Service Workers 的数据


Perfomance.now

高性能api通过其函数 performance.now() 提供对 DOMHighResTimeStamp 的访问,该函数返回自页面加载时间(以毫秒为单位),精度最高为 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');

输出(Chrome):

0.6350000001020817 "milliseconds"

输出(Firefox):

1 milliseconds

在这里,我们可以看到 Firefox 中的结果与 Chrome 完全不同。这是因为从版本 60 开始,Firefox 将 performance API 的精度降低到了 2ms。你可以在本文的末尾找到有关此内容的更多信息。

performance API 提供的功能比仅返回时间戳要多得多。它可以测量导航时间、用户时间或资源时间。查看本文,其中有更详细的说明。

但是对于我们的用例,只想测量单个函数的性能,因此时间戳就足够了。

与 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 个计时器。

精度与 performance API 相同,但这又取决于浏览器。

console.time('test');
for (let i = 0; i < array.length; i++) {
  // some code
}
console.timeEnd('test');

这将会自动生成人类可读的输出,如下所示:

输出(Chrome):

test: 0.766845703125ms

输出(Firefox):

test: 2ms - timer ended

此处的输出仍然与 Performance API 非常相似。

console.time 的优点是容易使用,因为它不需要手动计算两个时间戳之间的差。


时间精度降低

如果你在不同的浏览器中使用上述API来评估函数,你可能会注意到结果会有所不同

这是由于浏览器试图保护用户免受 timing 攻击 和指纹识别,如果时间戳过于准确,黑客可以使用它来识别用户。

像 Firefox 这样的浏览器试图通过把精度降低到 2ms(60版)来防止这种情况。


注意事项

现在你已经拥有了测量 JavaScript 函数运行速度所需的工具。但是还要避免一些陷阱:

分而治之

在筛选某些结果时发现速度很慢,但你不知道瓶颈在哪里。

你可以用上面提到的这些函数来度量代码,而不必去猜测到底史哪一部分代码慢。

首先要跟踪它,把 console.time 语句放在执行缓慢的代码块前后。然后评估他们不同部分的表现。如果一个比另一个慢,那就继续往下走,直到发现瓶颈为止。

这些语句之间的代码越少,则跟踪到不感兴趣的内容的可能性就越小。

注意输入值

在实际应用中,给定函数的输入值可能会发生很大变化。如果仅针对任意随机值测量函数,那么速度并不能为我们提供任何有实用价值的数据。

要确保运行代码时使用的输入值是相同的。

多次运行函数

假设有一个函数可以遍历数组,并对每个值进行一些计算,然后返回包含结果的数组。你想知道 forEach 或简单的 for 循环哪个更有效。

这些是函数:

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。

...还有在多个浏览器中

如果我们在 Chrome 中运行上述代码,结果会突然看起来不同:

test-forEach: 6.156005859375ms
test-forEach: 8.01416015625ms
test-for: 4.371337890625ms
test-for: 4.31298828125ms

这是因为 Chrome 和 Firefox 的 JavaScript 引擎是不同的,并且性能优化的类型也不同。能够意识到这些差异是一件好事。

在这种情况下,Firefox 的优化在 forEach 方面做得比 Chrome 更好。

for 在两个引擎上的性能都更好,所以最好坚持 for 循环。

这是一个很好的例子,说明了为什么应该在多个引擎中进行测量。如果仅用 Chrome 进行测量,你可能会得出:forEach 相对于 for 而言还算不错这样的结论。

限制你的 CPU

请注意,你的开发机器通常比浏览你网站的普通手机要快得多。

浏览器具有一项功能,可让你限制 CPU 性能。这样的话,10 或 50 毫秒很快就会变成500毫秒。

衡量相对表现

实际上这些结果不仅取决于你的硬件,还取决于你的 CPU 和当前 JavaScript 线程的负载。尝试在不同情况下进行测量,因为下次你重新启动计算机时,你得到的数字看起来可能会大不相同。


结论

在本文中,我们看到了一些 JavaScript API,可以使用它们来衡量性能,以及如何在“真实世界”中使用它们。对于简单的测量,我发现用 console.time 更容易。

我觉得很多前端开发人员普遍没有对性能进行足够的考虑,即使这对你的收入有直接的影响。

作者:Felix Gerschau
翻译:疯狂的技术
原文:https://felixgerschau.com/

链接: https://fly63.com/article/detial/8497

提高js加载速度,实现js无阻塞加载方式,高性能的加载执行JavaScript

为解决JS加载速度慢,采用js的延时加载,和动态加载。由于js的堵塞特性,当浏览器在加载javascript代码时,不能同时做其他任何事情,如果javascript执行时间越久,浏览器等待响应的时间就越久。

如何提高CSS性能?CSS优化、提高性能提升总汇

如何提高CSS性能,根据页面的加载性能和CSS代码性能,主要表现为: 加载性能 (主要是从减少文件体积,减少阻塞加载,提高并发方面入手),选择器性能,渲染性能,可维护性。

前端性能优化_css加载会造成哪些阻塞现象?

css的加载是不会阻塞DOM的解析,但是会阻塞DOM的渲染,会阻塞link后面js语句的执行。这是由于浏览器为了防止html页面的重复渲染而降低性能,所以浏览器只会在加载的时候去解析dom树,然后等在css加载完成之后才进行dom的渲染以及执行后面的js语句。

2018 前端性能检查表

性能十分重要。然而,我们真的知道性能瓶颈具体在哪儿吗?是执行复杂的 JavaScript,下载缓慢的 Web 字体,巨大的图片,还是卡顿的渲染?研究摇树(Tree Shaking),作用域提升(Scope Hoisting)

高性能Javascript总结

Js高性能总结:加载和运行、数据访问、DOM编程、算法和流程控制、响应接口、Ajax 异步JavaScript和XML、编程实践...

优化网站性能规则_前端性能优化策略【网络加载、页面渲染】

前端网站性能优化规则:网络加载类、页面渲染类。包括:减少 HTTP 资源请求次数、减小 HTTP 请求大小、避免页面中空的 href 和 src、合理设置 Etag 和 Last-Modified、使用可缓存的 AJAX、减少 DOM 元素数量和深度等

前端性能的本质是什么?

性能一直以来是前端开发中非常重要的话题。随着前端能做的事情越来越多,浏览器能力被无限放大和利用:从 web 游戏到复杂单页面应用,从 NodeJS 服务到 web VR/AR 和数据可视化,前端工程师总是在突破极限

BigPipe_高性能流水线页面技术

BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。

用CSS开启硬件加速来提高网站性能

你知道我们可以在浏览器中用css开启硬件加速,使GPU (Graphics Processing Unit) 发挥功能,从而提升性能吗?现在大多数电脑的显卡都支持硬件加速。鉴于此,我们可以发挥GPU的力量,从而使我们的网站或应用表现的更为流畅。

原生js实现懒加载并节流

像淘宝网站等,页面中有着大量图片,一次性全部加载这些图片会使浏览器发送大量请求和造成浪费。采用懒加载技术,即用户浏览到哪儿,就加载该处的图片。这样节省网络资源、提升用户体验、减少服务器压力。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!