测量JavaScript函数的性能各种方式对比
概述
测量执行一个函数所需的时间总是一个很好的办法,证明某些实现比另一个实现的性能更好。这也是一个很好的方法,可以确保性能没有在某些改变后受到影响,也可以追踪瓶颈。
良好的性能有助于获得良好的用户体验,良好的用户体验会让用户回头客。一项研究显示,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函数性能资料请关注猪先飞其它相关文章!
相关文章
- eval函数在php中是一个函数并不是系统组件函数,我们在php.ini中的disable_functions是无法禁止它的,因这他不是一个php_function哦。 eval()针对php安全来说具有很...2016-11-25
- 在php中eval是一个函数并且不能直接禁用了,但eval函数又相当的危险了经常会出现一些问题了,今天我们就一起来看看eval函数对数组的操作 例子, <?php $data="array...2016-11-25
使用PHP+JavaScript将HTML页面转换为图片的实例分享
这篇文章主要介绍了使用PHP+JavaScript将HTML元素转换为图片的实例分享,文后结果的截图只能体现出替换的字体,也不能说将静态页面转为图片可以加快加载,只是这种做法比较interesting XD需要的朋友可以参考下...2016-04-19Python astype(np.float)函数使用方法解析
这篇文章主要介绍了Python astype(np.float)函数使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-08- 这篇文章主要介绍了Python中的imread()函数用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-16
- 在昨天的《Javascript权威指南》学习笔记之十:ECMAScript 5 增强的对象模型一文中,对于一段代码的调试出现了一个奇怪现象,现将源代码贴在下面: 复制代码 代码如下: <script type="text/javascript"> function Person(){}...2014-05-31
- 最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
- 本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
- 下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 复制代码 代码如下: //element:需要添加新样式的元素,value:新的样式 function addClass(element, value ){ if (!element.className){ element.className = value; }else { newClassName = element.className; newClas...2014-05-31
- 在javascritp中,不一定只有对象方法的上下文中才有this, 全局函数调用和其他的几种不同的上下文中也有this指代。 它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下...2015-03-15
- 首先,我想到的是另建一个结果数组,用来存储原始数组中不重复的数据。遍历原始数组依次跟结果数组中的元素进行比较,检测是否重复。于是乎,我写出了如下代码A: Array.prototype.clearRepetitionA = function(){ var resul...2015-11-08
- 有一道js面试题,题目是这样的:下列代码的执行结果是什么,为什么? 复制代码 代码如下: var i, j, k; for (i=0, j=0; i<10, j<6; i++, j++) { k = i+j; } document.write(k); 答案是显示10,这道题主要考察JavaScript的逗...2015-03-15
- 事件触发器从字面意思上可以很好的理解,就是用来触发事件的,但是有些没有用过的朋友可能就会迷惑了,事件不是通常都由用户在页面上的实际操作来触发的吗?这个观点不完全正确,因为有些事件必须由程序来实现,如自定义事件,jQue...2014-06-07
- 1、ActiveX向Javascript传参 复制代码 代码如下: <script language="javascript" for="objectname" event="fun1(arg)"> fun2(arg); </script> objectname为ActiveX控件名,通过<object>标签里的id属性设定,如下; 复制...2014-06-07
- 这篇文章主要介绍了Javascript类型转换的规则实例解析,涉及到javascript类型转换相关知识,对本文感兴趣的朋友一起学习吧...2016-02-27
- 通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素。 HTML DOM (文档对象模型) 当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。 HTML DOM 模型被构造为对象的树: 通过可编程的对象模型,Java...2015-10-23
- Window有navigator对象让我们得知浏览器的全部信息.我们可以利用一系列的API函数得知浏览器的信息.JavaScript代码如下:function message(){ txt = "<p>浏览器代码名: " + navigator.appCodeName + "</p>";txt+= "<p>...2015-11-24
- CREATE FUNCTION ChangeBigSmall (@ChangeMoney money) RETURNS VarChar(100) AS BEGIN Declare @String1 char(20) Declare @String2 char...2016-11-25
- 虽然ES6都还没真正发布,但已经有用ES6重写的程序了,各种关于ES789的提议已经开始了,这你敢信。潮流不是我等大众所能追赶的。潮流虽然太快,但我们不停下学习的步伐,就不会被潮流丢下的,下面来领略下ES6中新特性,一堵新生代JS...2015-11-24