概述

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

良好的性能有助于获得良好的用户体验,良好的用户体验会让用户回头客。一项研究显示,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函数的性能各种方式对比的更多相关文章

  1. 整理CocosCreator常用知识点

    一、场景加载cc.director.loadScene(‘场景名称');//场景跳转cc.director.preloadScene(‘场景名称');//预加载场景cc.director.getScene();//获取当前场景二、查找节点1,节点查找node = cc.find(“Canvas/bg......

  2. javascript 数组(list)添加/删除的实现

    javascript 数组Array(list)添加/删除unshift:将参数添加到原数组开头,并返回数组的长度pop:删除原数组最后一项,并返回删除元素的值;如果数组为空则返回undefinedpush:将参数添加到原数组末尾,并返回数组的长度concat:返回一个新数组,是将参数添加到原数组中......

  3. JavaScript实现动态加载删除表格

    本文实例为大家分享了JavaScript实现动态加载删除表格的具体代码,供大家参考,具体内容如下代码:<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"......

  4. jQuery实现鼠标拖动div改变位置、大小的实践

    实现类似windows窗体的效果,在中间拖动改变div位置,在边缘拖动改变div大小,鼠标在相应的位置改变成相应的形状效果如图: (截图没显示鼠标)代码如下:$(".test1").mousemove(function(e){$(".test1").unbin......

  5. jQuery+css实现的点击图片放大缩小预览功能示例【图片预览 查看大图】

    本文实例讲述了jQuery+css实现的点击图片放大缩小预览功能。分享给大家供大家参考,具体如下:要求点击一张图片,图片就会放大,查看大图,点击空白处就会隐藏大图,回到缩略图。技术要点主要是Jquery进行元素的显示与隐藏。代码qqq*{margin:0;padding:0;}h1{text-ali......

  6. JavaScript——深入了解this

    前言我曾以为func()其实就是window.func()function func(){console.log('this : ' + this);}func();//this : [object Window]window.func();//this : [object Window] 直到'u......

  7. 详解CocosCreator中几种计时器的使用方法

    一、setTimeOut3秒后打印abc。只执行一次。setTimeout(()=>{console.log("abc"); }, 3000);删除计时器,3秒后不会输出abc。let timeIndex;timeIndex = setTimeout(()=>{con......

  8. 如何使JavaScript休眠或等待

    概述JavaScript不具有 sleep()函数,该函数会导致代码在恢复执行之前等待指定的时间段。如果需要JavaScript等待,该怎么做呢?假设您想将三则消息记录到Javascript控制台,每条消息之间要延迟一秒钟。JavaScript中没有 sleep() 方法,所以你可以尝试使用下一个最......

  9. 浅谈JavaScript代码性能优化2

    一.减少判断层级 从下图代码中可以明显看出,同样的效果判断层级的减少可以优化性能二.减少作用域链查找层级 简单解释下,下图中第一个运行foo函数,bar函数内打印name,bar作用域内没有name,所以作用域链往上查找,foo内部有name,但是这种写法实际上是赋值,还要往上查找,全局作用域中查询......

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

    概述测量执行一个函数所需的时间总是一个很好的办法,证明某些实现比另一个实现的性能更好。这也是一个很好的方法,可以确保性能没有在某些改变后受到影响,也可以追踪瓶颈。良好的性能有助于获得良好的用户体验,良好的用户体验会让用户回头客。一项研究显示,88%的在线消费者因为性能问题,在用户体验不佳后用户回来的......

随机推荐

  1. 编写python程序的90条建议

    1. 首先建议1、理解 Pythonic 概念—-详见 Python 中的《Python之禅》建议2、编写 Pythonic 代码(1)避免不规范代码,比如只用大小写区分变量、使用容易混淆的变量名、害怕过长变量名等。有时候长的变量名会使代码更加具有可读性。(2)深入学习 Python 相关知识,比如......

  2. 利用Python函数实现一个万历表完整示例

    前言大家可以根据格式化打印字符去调一下最后的输出,不过有中文好像不好调整,可以换成星期的单词,这样应该会好一点,format()函数可以用来格式化打印字符,format()可以使用字符串去调用,也可以独自使用。可以点进格式化打印字符了解一下哦示例代码# 判断是否闰年def isleap(year):......

  3. java中instanceof 关键字作用和实际用途详解

    instanceof1. instanceof 是Java中的一个关键字,Java中的关键子都是小写。2. instanceof关键字的作用是判断左边对象是否是右边类(这里有很多人说是对象,所以注意这里是类,并不是对象)的实例(通俗易懂的说就是:子类对象,或者右边类本身的对象)返回的boolean类......

  4. OpenCV实现简易标定板

    本文实例为大家分享了OpenCV实现简易标定板的具体代码,供大家参考,具体内容如下使用OpenCV生成标定板图片,然后找高精度打印机进行打印,贴在硬板上,就可以得到一个简易的标定板。废话不多说,代码如下://编程环境:VS2013, X64,OpenCV3.0.0#include <iostr......

  5. R语言决策基础知识点详解

    决策结构要求程序员指定要由程序评估或测试的一个或多个条件,以及如果条件被确定为真则要执行的一个或多个语句,如果条件为假则执行其他语句。以下是在大多数编程语言中的典型决策结构的一般形式R提供以下类型的决策语句。 单击以下链接以检查其详细信息。Sr.No.声明和描述1if语句if语句由一个布尔表达式后跟......

  6. vue中data改变后让视图同步更新的方法

    前言不久前天看到一个比较有趣的问题,vue中data改变后,如何让视图同步更新,搜索了一下,并没有发现解决问题的方法,只能从源码去找解决方法了。原因我们都知道,在vue中改变数据后,视图并不是同步更新的。在vue实例初始化后,会将data设置为响应式对象,当我们执行this.xxx = 1时,会触发......

  7. 如何在C#9 中使用顶级程序 (top-level)

    当我们用 C# 进行编码的时候,总需要写很多的模板代码,即使是最简单的 console 程序,想象一下,如果去测试一个 类库 或者 API 的功能,通常你会用 Console 程序去实现,在开始工作的时候会发现你受到了 C# 标准模板的限制,业务逻辑必须要写在 Main 里,如下代码所示: ......

  8. ES6在工作中会用到的核心知识点讲解

    一、var, let, const谈到ES6,估计大家首先肯定会想到var,let,const咱就先谈谈这三者的区别var a = 3;{var a = 4;}console.log(a);//4let b = 5;{let b = 6;}console.log(b);//5const c = 7;......

  9. JavaScript 中的六种循环方法

    Javascript中的遍历循环1.for循环对于数值索引的数组来说,可以使用标准的for循环来遍历值const arr=[1,2,3,4];for(let i=0;i2.for...in循环for...in循环可以用来遍历对象的可枚举属性列表(包括原型链上的属性)const myObject={}......

  10. python如何在word中存储本地图片

    想要利用Python来操作word文档可以使用docx模块.安装: pip install python-docxfrom docx import Documentfrom docx.shared import Inchesstring = '文字内容'images = '1.jpg' # 保存在本......