We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
译者:前端小智 作者:Felix Gerschau 来源:felixgerschau
点赞再看,微信搜索 【大迁世界】 关注这个没有大厂背景,但有着一股向上积极心态人。本文 GitHub https://github.com/qq449245884/xiaozhi 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。
GitHub
要比较两个函数哪个性能更好,一个直观且公平的方法就是计算两个函数分别执行完的时间。
良好的性能更容易好的用户体验,而好的用户体验更能留住用户。 研究表明,由于性能问题,在88%的在线消费者对用户体验不满意后,他们不太可能会二次使用。
88%
这也是为什么要提高性能的一个重要原因。 特别是使用 JS 开发时,编写的每一行 JS 都可能会阻塞DOM,因为它是单线程语言。
本次分享,我们主要介绍如何计算函数的性能。
Performance是一个做前端性能监控离不开的API,最好在页面完全加载完成之后再使用,因为很多值必须在页面完全加载之后才能得到。最简单的办法是在window.onload事件中读取各种数据。
Performance
window.onload
performance.now()方法返回一个精确到毫秒的 DOMHighResTimeStamp 。
performance.now()
DOMHighResTimeStamp
根据 MDN :
这个时间戳实际上并不是高精度的。为了降低像Spectre这样的安全威胁,各类浏览器对该类型的值做了不同程度上的四舍五入处理。(Firefox从Firefox 59开始四舍五入到2毫秒精度)一些浏览器还可能对这个值作稍微的随机化处理。这个值的精度在未来的版本中可能会再次改善;浏览器开发者还在调查这些时间测定攻击和如何更好的缓解这些攻击。
因为,要计算一个函数的执行时间,分别比较函数执行前和执行后的两次 performance.now()的值即可,如下所示:
const t0 = performance.now(); for (let i = 0; i < array.length; i++) { // some code } const t1 = performance.now(); console.log(t1 - t0, 'milliseconds');
在这里,我们可以看到 Firefox 中的结果与 Chrome 完全不同。 这是因为从版本60开始,Firefox 将performance API的精度降低到2ms。
60
2ms
performance API 不当当只有返回时间戳这个功能,还有很多实用方法,大家可以根据需要到 MDN 查询相关的文档。
然而,对于我们的用例,我们只想计算单个函数的性能,因此时间戳就足够了。
performance.now() 和 Date.now一样吗?
你可能会想,嘿,我也可以使用Date.now来做?
Date.now
是的,你可以,但这有缺点。
Date.now返回自Unix纪元(1970-01-01T00:00:00Z)以来经过的时间(以毫秒为单位),并取决于系统时钟。 这不仅意味着它不够精确,而且还不总是递增。 WebKit工程师(Tony Gentilcore)的解释如下:
基于系统时间的日期可能不太会被采用,对于实际的用户监视也不是理想的选择。 大多数系统运行一个守护程序,该守护程序定期同步时间。 通常每15至20分钟将时钟调整几毫秒。 以该速率,大约10秒间隔的1%将是不准确的。
除了Performance.now函数外,还有一些函数可以让我们度量代码不同部分的时间,并将它们作为性能测试工具(如Webpagetest)中的自定义度量。
Performance.now
Webpagetest
先来看看MDN中关于mark方法的定义:
The mark() method creates a timestamp in the browser's performance entry buffer with the given name.
这段话可以分解出三个关键词。首先timestamp,这里的timestamp指的是高精度时间戳(千分之一毫秒),其次是performance entry buffer。
timestamp
performance entry buffer指的是存储performance实例对象的区域,初始值为空。
performance
最后就是given name,表示生成的每一个timestamp都有相应的名称。
所以这句话就可以理解成,在浏览器的performance entry buffer中,根据名称生成高精度时间戳。也就是很多人说过的**“打点”**。
就像Performance.now一样,此函数的精度分数高达5µs。
Performance.no
5µs
performance.mark('name');
标记 的 performance entry将具有以下属性值:
entryType - 设置为 "mark".
entryType
name - 设置为mark被创建时给出的 "name"
name
startTime - 设置为 mark() 方法被调用时的 timestamp 。
startTime
为 mark()
duration - 设置为 "0" (标记没有持续时间).
duration
同样先来看看 MDN 上关于 measure 的定义:
这段定义和上面 mark 的定义有些类似,其最核心的不同点在于这句话 between two specified marks。所以measure是指定两个mark点之间的时间戳。如果说mark可以理解为**"打点"的话,measure就可以理解为"连线"**。
mark
between two specified marks
measure
performance.measure(name, startMark, endMark);
计算两个mark之间的时长,创建一个DOMHighResTimeStamp保存在资源缓存数据中,可通过performance.getEntries()等相关接口获取。
performance.getEntries()
从导航开始测量
performance.measure('measure name');
导航开始到标记
performance.measure('measure name', undefined, 'mark-2');
从标记到标记
performance.measure('measure name', 'mark-1', 'mark-2');
在上面的函数中,总是提到结果存储在performance entry buffer,但是如何访问其中的内容呢?
performance entry buffer
performance API有3个函数可以用来访问该数据:
获取一组当前页面已经加载的资源PerformanceEntry对象。接收一个可选的参数options进行过滤,options支持的属性有name,entryType,initiatorType。
options
initiatorType
let entries = window.performance.getEntries();
performance.getEntriesByName
根据参数name,type获取一组当前页面已经加载的资源数据。name的取值对应到资源数据中的name字段,type取值对应到资源数据中的entryType字段。
type
let entries = window.performance.getEntriesByName(name, type);
performance.getEntriesByType
根据参数type获取一组当前页面已经加载的资源数据。type取值对应到资源数据中的entryType字段。
var entries = window.performance.getEntriesByType(type);
结合事例:
performance.mark('mark-1'); // some code performance.mark('mark-2') performance.measure('test', 'mark-1', 'mark-2') console.log(performance.getEntriesByName('test')[0].duration);
这个 API确实易于使用。当需要统计一段代码的执行时间时,可以使用console.time方法与console.timeEnd方法,其中console.time方法用于标记开始时间,console.timeEnd方法用于标记结束时间,并且将结束时间与开始时间之间经过的毫秒数在控制台中输出。这两个方法的使用方法如下所示。
console.time
console.timeEn
console.timeEnd
console.time('test'); for (let i = 0; i < array.length; i++) { // some code } console.timeEnd('test');
输出的结果与Performance API非常相似。
console.time的优点是易于使用,因为它不需要手动计算两个时间戳之间的差。
如果在不同的浏览器中使用上面提到的 api 测量函数,你可能会注意到结果是不同的。
这是由于浏览器试图保护用户免受时序攻击(timing attack)和指纹采集(Fingerprinting ),如果时间戳过于准确,黑客可以使用它们来识别用户。
例如,Firefox等浏览器试图通过将精度降低到2ms(版本60)来防止这种情况发生。
现在,我们已经知道了要测量JavaScript函数的速度所需方法。 但是,最好还要避免一些陷阱:
开发过程中,我们可能会我发现有些模块执行速度很慢,但是我们不知道具体问题出在哪里。解决一个方法是,使用上面提到的这些函数来测量它,而不是胡乱猜测代码的哪一部分比较慢。
要对其进行跟踪,首先将console.time语句放在执行比较慢的代码块周围。 然后测量它们不同部分的表现。 如果一个比另一个慢,那就继续往下走,直到发现问题所在。
在实际应用中,给定函数的输入值可能会发生很大变化。 仅针对任意随机值测量函数的速度并不能提供我们可以实际使用的任何有价值的数据。
确保使用相同的输入值运行代码。
假设你有一个函数,它的功是遍历一个数组,对数组的每个值进行一些计算,然后返回一个带有结果的数组。你想知道是forEach循环还是简单的for循环性能更好。
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 中运行上述函数,结果:
看起来forEach慢多了,对吧?
那如果是相同的输入,运行两次呢:
testForEach(x); testForEach(x); testFor(x); testFor(x);
如果我们第二次调用forEach测试,它的执行效果和for循环一样好。考虑到初始值较慢,在一些性能要求极高的项目,可能就不适合使用forEach。
如果我们在Chrome中运行上述代码,结果又会不一样:
这是因为Chrome和Firefox具有不同的JavaScript引擎,它们具有不同类型的性能优化。
在本例中,Firefox 在对相同输入的forEach进行优化方面做得更好。
for在两个引擎上的性能都更好,因此在一些性能要求极高的项目就需要使用for循环。
这是为什么要在多个引擎中进行测量的一个很好的例子。 如果仅使用Chrome进行测量,你可能会得出结论,与for相比,forEach并不那么糟糕。
Chrome
我们在本地测试值是不能代表用户在浏览器使用的情况,因为 我们开发的电脑一般都会比大部分的用户好很多。
浏览器有一个特性可以限制CPU性能,我们通过设置可以更贴切一些真实情况。
在本文中,我们看到了一些JavaScript API,我们可以使用它们来衡量性能,以及如何在真实的项目中使用它们。 对于简单的测量,我发现使用console.time更容易。 如果要将测量与性能测量工具集成在一起,则可能需要使用performance.mark和performance.measure。
performance.mark
performance.measure
人才们的 【三连】 就是小智不断分享的最大动力,如果本篇博客有任何错误和建议,欢迎人才们留言,最后,谢谢大家的观看。
编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://felixgerschau.com/measuring-the-performance-of-java-script-functions
文章每周持续更新,可以微信搜索 【大迁世界 】 第一时间阅读,回复 【福利】 有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
要比较两个函数哪个性能更好,一个直观且公平的方法就是计算两个函数分别执行完的时间。
良好的性能更容易好的用户体验,而好的用户体验更能留住用户。 研究表明,由于性能问题,在
88%
的在线消费者对用户体验不满意后,他们不太可能会二次使用。这也是为什么要提高性能的一个重要原因。 特别是使用 JS 开发时,编写的每一行 JS 都可能会阻塞DOM,因为它是单线程语言。
本次分享,我们主要介绍如何计算函数的性能。
Performance.now
Performance
是一个做前端性能监控离不开的API,最好在页面完全加载完成之后再使用,因为很多值必须在页面完全加载之后才能得到。最简单的办法是在window.onload
事件中读取各种数据。performance.now()
方法返回一个精确到毫秒的DOMHighResTimeStamp
。根据 MDN :
因为,要计算一个函数的执行时间,分别比较函数执行前和执行后的两次
performance.now()
的值即可,如下所示:在这里,我们可以看到 Firefox 中的结果与 Chrome 完全不同。 这是因为从版本
60
开始,Firefox 将performance API的精度降低到2ms
。performance API 不当当只有返回时间戳这个功能,还有很多实用方法,大家可以根据需要到 MDN 查询相关的文档。
然而,对于我们的用例,我们只想计算单个函数的性能,因此时间戳就足够了。
performance.now() 和 Date.now一样吗?
你可能会想,嘿,我也可以使用
Date.now
来做?是的,你可以,但这有缺点。
Date.now
返回自Unix纪元(1970-01-01T00:00:00Z)以来经过的时间(以毫秒为单位),并取决于系统时钟。 这不仅意味着它不够精确,而且还不总是递增。 WebKit工程师(Tony Gentilcore)的解释如下:Performance.mark 和 Performance.measure
除了
Performance.now
函数外,还有一些函数可以让我们度量代码不同部分的时间,并将它们作为性能测试工具(如Webpagetest
)中的自定义度量。Performance.mark
先来看看MDN中关于mark方法的定义:
这段话可以分解出三个关键词。首先
timestamp
,这里的timestamp
指的是高精度时间戳(千分之一毫秒),其次是performance entry buffer。performance entry buffer指的是存储
performance
实例对象的区域,初始值为空。最后就是given name,表示生成的每一个
timestamp
都有相应的名称。所以这句话就可以理解成,在浏览器的performance entry buffer中,根据名称生成高精度时间戳。也就是很多人说过的**“打点”**。
就像
Performance.no
w一样,此函数的精度分数高达5µs
。标记 的 performance entry将具有以下属性值:
entryType
- 设置为 "mark".name
- 设置为mark被创建时给出的 "name"startTime
- 设置为 mark()
方法被调用时的 timestamp 。duration
- 设置为 "0" (标记没有持续时间).Performance.measure
同样先来看看 MDN 上关于 measure 的定义:
这段定义和上面
mark
的定义有些类似,其最核心的不同点在于这句话between two specified marks
。所以measure是指定两个mark
点之间的时间戳。如果说mark
可以理解为**"打点"的话,measure
就可以理解为"连线"**。计算两个mark之间的时长,创建一个
DOMHighResTimeStamp
保存在资源缓存数据中,可通过performance.getEntries()
等相关接口获取。entryType
为字符串measure
name
为创建时设置的值startTime
为调用 measure 时的时间duration
为两个 mark 之间的时长从导航开始测量
导航开始到标记
从标记到标记
资源性能数据
从 performance entry buffer 获取数据
在上面的函数中,总是提到结果存储在
performance entry buffer
,但是如何访问其中的内容呢?performance API有3个函数可以用来访问该数据:
performance.getEntries()
获取一组当前页面已经加载的资源PerformanceEntry对象。接收一个可选的参数
options
进行过滤,options
支持的属性有name
,entryType
,initiatorType
。performance.getEntriesByName
根据参数
name
,type
获取一组当前页面已经加载的资源数据。name
的取值对应到资源数据中的name
字段,type
取值对应到资源数据中的entryType
字段。performance.getEntriesByType
根据参数
type
获取一组当前页面已经加载的资源数据。type
取值对应到资源数据中的entryType
字段。结合事例:
Console.time
这个 API确实易于使用。当需要统计一段代码的执行时间时,可以使用
console.time
方法与console.timeEn
d方法,其中console.time
方法用于标记开始时间,console.timeEnd
方法用于标记结束时间,并且将结束时间与开始时间之间经过的毫秒数在控制台中输出。这两个方法的使用方法如下所示。输出的结果与Performance API非常相似。
console.time
的优点是易于使用,因为它不需要手动计算两个时间戳之间的差。减少时间精度
如果在不同的浏览器中使用上面提到的 api 测量函数,你可能会注意到结果是不同的。
这是由于浏览器试图保护用户免受时序攻击(timing attack)和指纹采集(Fingerprinting ),如果时间戳过于准确,黑客可以使用它们来识别用户。
例如,Firefox等浏览器试图通过将精度降低到2ms(版本60)来防止这种情况发生。
注意事项
现在,我们已经知道了要测量JavaScript函数的速度所需方法。 但是,最好还要避免一些陷阱:
分而治之
开发过程中,我们可能会我发现有些模块执行速度很慢,但是我们不知道具体问题出在哪里。解决一个方法是,使用上面提到的这些函数来测量它,而不是胡乱猜测代码的哪一部分比较慢。
要对其进行跟踪,首先将
console.time
语句放在执行比较慢的代码块周围。 然后测量它们不同部分的表现。 如果一个比另一个慢,那就继续往下走,直到发现问题所在。注意输入值
在实际应用中,给定函数的输入值可能会发生很大变化。 仅针对任意随机值测量函数的速度并不能提供我们可以实际使用的任何有价值的数据。
确保使用相同的输入值运行代码。
多次运行该函数
假设你有一个函数,它的功是遍历一个数组,对数组的每个值进行一些计算,然后返回一个带有结果的数组。你想知道是
forEach
循环还是简单的for
循环性能更好。然后这样测试它们:
如果在 Firefox 中运行上述函数,结果:
看起来
forEach
慢多了,对吧?那如果是相同的输入,运行两次呢:
如果我们第二次调用
forEach
测试,它的执行效果和for
循环一样好。考虑到初始值较慢,在一些性能要求极高的项目,可能就不适合使用forEach
。在多个浏览器中测试
如果我们在Chrome中运行上述代码,结果又会不一样:
这是因为Chrome和Firefox具有不同的JavaScript引擎,它们具有不同类型的性能优化。
在本例中,Firefox 在对相同输入的
forEach
进行优化方面做得更好。for
在两个引擎上的性能都更好,因此在一些性能要求极高的项目就需要使用for
循环。这是为什么要在多个引擎中进行测量的一个很好的例子。 如果仅使用
Chrome
进行测量,你可能会得出结论,与for
相比,forEach
并不那么糟糕。限制的 CPU
我们在本地测试值是不能代表用户在浏览器使用的情况,因为 我们开发的电脑一般都会比大部分的用户好很多。
浏览器有一个特性可以限制CPU性能,我们通过设置可以更贴切一些真实情况。
总结
在本文中,我们看到了一些JavaScript API,我们可以使用它们来衡量性能,以及如何在真实的项目中使用它们。 对于简单的测量,我发现使用
console.time
更容易。 如果要将测量与性能测量工具集成在一起,则可能需要使用performance.mark
和performance.measure
。人才们的 【三连】 就是小智不断分享的最大动力,如果本篇博客有任何错误和建议,欢迎人才们留言,最后,谢谢大家的观看。
编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://felixgerschau.com/measuring-the-performance-of-java-script-functions
交流
文章每周持续更新,可以微信搜索 【大迁世界 】 第一时间阅读,回复 【福利】 有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。
The text was updated successfully, but these errors were encountered: