测量JavaScript函数的性能各种方式对比


Posted in Javascript onApril 27, 2021

概述

测量执行一个函数所需的时间总是一个很好的办法,证明某些实现比另一个实现的性能更好。这也是一个很好的方法,可以确保性能没有在某些改变后受到影响,也可以追踪瓶颈。

良好的性能有助于获得良好的用户体验,良好的用户体验会让用户回头客。一项研究显示,88%的在线消费者因为性能问题,在用户体验不佳后用户回来的可能性较小。

这就是为什么能够识别代码中的瓶颈并测量改进的原因。尤其是在为浏览器开发JavaScript时,要注意到你写的每一行JavaScript都有可能阻塞DOM,因为它是一种单线程语言。

在这篇文章中,我将解释你如何测量你的功能的性能,以及如何处理你从它们中得到的结果。

Perfomance.now

performance 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完全不同,这是因为Firefox版本从60开始将 performance API 的精度降低到2ms。

performance API提供的功能远比只返回时间戳要多得多,它能够测量导航计时、用户计时或资源计时。请看这篇文章,里面有更详细的解释。

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

那不是和Date.now一样吗?

现在你可能会想:我也可以用Date.now来做这个啊。

是的,可以,但是有缺点。

Date.now以毫秒为单位返回从Unix纪元("1970-01-01-01T00:00:00:00Z")开始的时间,并且取决于系统时钟。这不仅意味着它没有那么精确,而且也不一定会递增。WebKit工程师(Tony Gentilcore)的解释如下:

也许较少考虑到的是,基于系统时间的Date也不是真正的用户监控的理想选择。大多数系统都会运行一个守护进程来定期同步时间。通常情况下,时钟每隔15-20分钟就会调整几毫秒。在这个速度下,大约有1%的10秒的时间间隔是不准确的。

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来测量你的函数,你可能会发现结果会有差异。

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

例如,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的使用进行了较好的优化。

for在两个引擎上的性能都更好,因此最好坚持使用for循环。

这是为什么要在多个引擎中进行测量的一个很好的例子。如果仅使用Chrome进行测量,您可能会得出结论,与for相比,forEach并不那么糟糕。

节流你的CPU

这些数值看起来并不高。要知道,你的开发机器通常比你的网站所使用的普通手机浏览速度要快得多。

为了感受一下这个样子,浏览器有一个功能,可以让你节流你的CPU性能。

有了这个,那些10或50ms很快就变成了500ms。

测量相对表现

这些原始结果实际上不仅仅取决于你的硬件,还取决于你的CPU和你的JavaScript线程的当前负载。尽量关注你的测量结果的相对改进,因为下次重启电脑时,这些数字可能会看起来很不一样。

总结

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

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

以上就是测量JavaScript函数的性能各种方式对比的详细内容,更多关于JavaScript函数性能资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
JS仿百度搜索自动提示框匹配查询功能
Nov 21 Javascript
extjs 分页使用jsp传递数据示例
Jul 29 Javascript
jQuery判断checkbox选中状态
May 12 Javascript
jquery解析XML及获取XML节点名称的实现代码
May 18 Javascript
JS获取地址栏参数的两种方法(简单实用)
Jun 14 Javascript
AngularJS1.X学习笔记2-数据绑定详解
Apr 01 Javascript
Vue 2.0学习笔记之Vue中的computed属性
Oct 16 Javascript
js实现手机web图片左右滑动效果
Dec 29 Javascript
JavaScript中toLocaleString()和toString()的区别实例分析
Aug 14 Javascript
深入理解移动前端开发之viewport
Oct 19 Javascript
JavaScript之数组扁平化详解
Jun 03 Javascript
如何使JavaScript休眠或等待
Apr 27 Javascript
比较node.js和Deno
Apr 27 #Javascript
如何用JavaScript检测当前浏览器是无头浏览器
Apr 27 #Javascript
如何利用js在两个html窗口间通信
Apr 27 #Javascript
如何使JavaScript休眠或等待
Apr 27 #Javascript
JavaScript 实现页面滚动动画
如何用JS实现网页瀑布流布局
分享几个JavaScript运算符的使用技巧
Apr 24 #Javascript
You might like
PHP的一个完整SMTP类(解决邮件服务器需要验证时的问题)
2006/10/09 PHP
php下过滤html代码的函数 提高程序安全性
2010/03/02 PHP
PHP的一个基础知识 表单提交
2011/07/04 PHP
详解PHP中strlen和mb_strlen函数的区别
2014/03/07 PHP
PHP实现的英文名字全拼随机排号脚本
2014/07/04 PHP
Zend Framework教程之动作的基类Zend_Controller_Action详解
2016/03/07 PHP
Zend Framework教程之Application用法实例详解
2016/03/14 PHP
PHP实现数组和对象的相互转换操作示例
2019/03/20 PHP
js 获取class的元素的方法 以及创建方法getElementsByClassName
2013/03/11 Javascript
JS实现金额转换(将输入的阿拉伯数字)转换成中文的实现代码
2013/09/30 Javascript
微信小程序 swiper组件构建轮播图的实例
2017/09/20 Javascript
JS处理数据四舍五入(tofixed与round的区别详解)
2017/10/26 Javascript
JS canvas绘制五子棋的棋盘
2020/05/28 Javascript
three.js实现圆柱体
2018/12/30 Javascript
vue实现浏览器全屏展示功能
2019/11/27 Javascript
微信小程序实现页面监听自定义组件的触发事件
2020/11/01 Javascript
[04:41]2014DOTA2国际邀请赛 Liquid顺利突围晋级正赛
2014/07/09 DOTA
[32:56]完美世界DOTA2联赛PWL S3 Rebirth vs CPG 第二场 12.11
2020/12/16 DOTA
Python装饰器使用示例及实际应用例子
2015/03/06 Python
pandas实现选取特定索引的行
2018/04/20 Python
python3 遍历删除特定后缀名文件的方法
2018/04/23 Python
Python完全识别验证码自动登录实例详解
2019/11/24 Python
在Tensorflow中实现梯度下降法更新参数值
2020/01/23 Python
基于Pytorch SSD模型分析
2020/02/18 Python
python 安装库几种方法之cmd,anaconda,pycharm详解
2020/04/08 Python
Python 合并拼接字符串的方法
2020/07/28 Python
Python实现小黑屋游戏的完整实例
2021/01/06 Python
澳大利亚最好的厨具店:Kitchen Warehouse
2018/03/13 全球购物
开业庆典邀请函
2014/01/08 职场文书
社区庆中秋节活动方案
2014/02/07 职场文书
党的群众路线教育实践活动党员个人剖析材料
2014/10/08 职场文书
2015年挂职锻炼工作总结
2014/12/12 职场文书
幼儿园教师自荐书
2015/03/06 职场文书
详解CSS玩转图片Base64编码
2021/05/25 HTML / CSS
SpringBoot整合JWT的入门指南
2021/06/29 Java/Android
详解Mysq MVCC多版本的并发控制
2022/04/29 MySQL