测试 JavaScript 函数的性能

症结1 – 意外衡量不重大的职业

在地点的以身作则中,你能够小心到,大家在三遍调用performance.now()中间只调用了makeHash()函数,然后将它的值赋给result变量。那给我们提供了函数的实践时间,而尚未任何的搅扰。咱们也得以依照下边包车型地铁艺术来度量代码的频率:

JavaScript

var t0 = performance.now(); console.log(makeHash(‘Peter’));  // bad
idea! var t1 = performance.now(); console.log(‘Took’, (t1 –
t0).toFixed(4), ‘milliseconds’);

1
2
3
4
var t0 = performance.now();
console.log(makeHash(‘Peter’));  // bad idea!
var t1 = performance.now();
console.log(‘Took’, (t1 – t0).toFixed(4), ‘milliseconds’);

以此代码片段的在线演示如下所示:

不过在这种景况下,大家将会衡量调用makeHash(‘Peter’)函数开销的岁月,以及将结果发送并打字与印刷到调节台上开支的时辰。大家不领悟那多少个操作中各类操作实际开支稍微时间,
只掌握放区救济总会的时间。何况,发送和打字与印刷输出的操作所费用的时日会借助于所用的浏览器,以致借助于当时的上下文。

恐怕你曾经完美的开采到console.log格局是不可以预测的。不过进行三个函数一样是荒谬的,即便各个函数都不会触发I/O操作。举个例子:

JavaScript

var t0 = performance.now(); var name = ‘Peter’; var result =
makeHash(name.toLowerCase()).toString(); var t1 = performance.now();
console.log(‘Took’, (t1 – t0).toFixed(4), ‘milliseconds to generate:’,
result);

1
2
3
4
5
var t0 = performance.now();
var name = ‘Peter’;
var result = makeHash(name.toLowerCase()).toString();
var t1 = performance.now();
console.log(‘Took’, (t1 – t0).toFixed(4), ‘milliseconds to generate:’, result);

一致,大家不会分晓推行时间是怎么布满的。它会是赋值操作、调用toLowerCase()函数可能toString()函数吗?

缺陷 #2 – 只衡量贰遍

其它三个分布的不当是只衡量一回,然后汇聚开销的年华,并以此得出结论。很可能推行不一的次数会得出完全两样的结果。推行时间依附于广大因素:

  • 编辑器热身的日子(例如,将代码编译成字节码的光阴)
  • 主线程恐怕正困苦其余一些大家从未察觉到的业务
  • 您的微型Computer的CPU只怕正忙辛苦碌一些会拖慢浏览器速度的政工

穿梭创新的点子是再度执行函数,就疑似那样:

JavaScript

var t0 = performance.now(); for (var i = 0; i < 10; i++) {
 makeHash(‘Peter’); } var t1 = performance.now(); console.log(‘Took’,
((t1 – t0) / 10).toFixed(4), ‘milliseconds to generate’);

1
2
3
4
5
6
var t0 = performance.now();
for (var i = 0; i < 10; i++) {
 makeHash(‘Peter’);
}
var t1 = performance.now();
console.log(‘Took’, ((t1 – t0) / 10).toFixed(4), ‘milliseconds to generate’);

以此示例的在线演示如下所示:

这种办法的高风险在于大家的浏览器的JavaScript引擎可能会选拔部分优化措施,那代表当大家第一遍调用函数时,假诺输入时同样的,那么JavaScript引擎大概会铭记了第三次调用的出口,然后轻易的归来这么些输出。为了解决这几个标题,你能够选拔过多见仁见智的输入字符串,而不用重新的施用同样的输入(举个例子‘Peter’)。明显,使用分裂的输入举行测量检验带来的标题就是大家衡量的函数会开支不一样的时刻。或许当中有的输入会费用比其他输入越来越长的施行时间。

Performance.now()

高分辨率时间API提供了贰个名叫now()的函数,它回到一个DOMHighRes提姆eStamp对象,那是贰个浮点数值,以皮秒等级(准确到少有皮秒)显示当前时刻。单独那么些数值并不会为你的剖判带来多少价值,可是三个这样的数值的差值,就能够准确描述过去了不怎么日子。

本条函数除了比内置的Date对象越来越准确以外,它如故“单调”的,轻易说,那象征它不会受操作系统(比如,你台式机上的操作系统)周期性修改系统时间影响。更简便的说,定义多个Date实例,计算它们的差值,并不意味过去了多少日子。

“单调性”的数学概念是“(四个函数也许数值)以未有减少或许没有扩张的主意退换”。

作者们能够从别的一种路子来讲解它,即想象使用它来在一年中让时钟向前恐怕向后转移。譬如,当您所在江山的机械原子钟都允许略过一个小时,以便最大化利用白天的时光。假若您在石英钟修改在此以前成立了二个Date实例,然后在修改现在创制了其它一个,那么查看那三个实例的差值,看上去大概像“1小时零3秒又123阿秒”。而选用五个performance.now()实例,差值会是“3秒又123阿秒456789之一阿秒”。

在这一节中,笔者不会提到那个API的过多细节。如若你想上学越多相关文化或查看越多怎么着采用它的事必躬亲,笔者建议您读书那篇文章:Discovering
the High Resolution Time
API。

既然如此你领悟高分辨率时间API是怎么样以及怎么着利用它,那么让大家承袭深切看一下它有哪些潜在的瑕玷。不过在此在此以前,大家定义贰个名叫makeHash()的函数,在那篇小说剩余的有个别,大家会使用它。

JavaScript

function makeHash(source) {  var hash = 0;  if (source.length === 0)
return hash;  for (var i = 0; i < source.length; i++) {    var char =
source.charCodeAt(i);    hash = ((hash<<5)-hash)+char;    hash =
hash & hash; // Convert to 32bit integer  }  return hash; }

1
2
3
4
5
6
7
8
9
10
function makeHash(source) {
 var hash = 0;
 if (source.length === 0) return hash;
 for (var i = 0; i < source.length; i++) {
   var char = source.charCodeAt(i);
   hash = ((hash<<5)-hash)+char;
   hash = hash & hash; // Convert to 32bit integer
 }
 return hash;
}

我们得以经过上边包车型大巴代码来衡量这几个函数的施行功用:

JavaScript

var t0 = performance.now(); var result = makeHash(‘Peter’); var t1 =
performance.now(); console.log(‘Took’, (t1 – t0).toFixed(4),
‘milliseconds to generate:’, result);

1
2
3
4
var t0 = performance.now();
var result = makeHash(‘Peter’);
var t1 = performance.now();
console.log(‘Took’, (t1 – t0).toFixed(4), ‘milliseconds to generate:’, result);

一旦您在浏览器中运转这几个代码,你应当看到类似上边包车型客车出口:

JavaScript

Took 0.2730 milliseconds to generate: 77005292

1
Took 0.2730 milliseconds to generate: 77005292

澳门太阳集团城网址,澳门太陽城集团登录网址,这段代码的在线演示如下所示:

铭记那一个示例后,让大家早先下边包车型客车座谈。

打赏支持小编翻译愈来愈多好小说,谢谢!

任选一种支付办法

澳门太阳集团城网址 1
澳门太阳集团城网址 2

1 赞 1 收藏
评论

至于作者:Wing

澳门太阳集团城网址 3

简要介绍还没赶趟写 :)
个人主页 ·
作者的稿子 ·
21 ·
   

澳门太阳集团城网址 4

缺陷 #4 – 以可预测的艺术相比函数

小编们早已清楚衡量一些函数很频仍并取平均值总会是二个好主意。並且,上边包车型大巴身体力行告诉大家使用中位数要比平均值更加好。

在实际上中,衡量函数实践时间的贰个很好的用处是来打探在多少个函数中,哪个更加快。假如大家有八个函数,它们的输入参数类型一致,输出结果一致,然则它们的中间贯彻机制不雷同。

举个例子,大家希望有一个函数,当特定的字符串在二个字符串数组中留存时,函数重回true可能false,但这几个函数在可比字符串时不关注大小写。换句话说,我们不可能直接行使Array.prototype.indexOf方法,因为这些格局是大大小小写敏感的。下边是这几个函数的二个达成:

JavaScript

function isIn(haystack, needle) {  var found = false;
 haystack.forEach(function(element) {    if (element.toLowerCase() ===
needle.toLowerCase()) {      found = true;    }  });  return found; }
console.log(isIn([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn([‘a’,’b’,’c’], ‘d’));  // false

1
2
3
4
5
6
7
8
9
10
11
12
function isIn(haystack, needle) {
 var found = false;
 haystack.forEach(function(element) {
   if (element.toLowerCase() === needle.toLowerCase()) {
     found = true;
   }
 });
 return found;
}
 
console.log(isIn([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn([‘a’,’b’,’c’], ‘d’));  // false

小编们能够马上开采这些情势有改良的地点,因为haystack.forEach循环总会遍历全数的成分,纵然大家能够急速找到一个同盟的因素。现在让我们选择for循环来编排一个越来越好的版本。

JavaScript

function isIn(haystack, needle) {  for (var i = 0, len =
haystack.length; i < len; i++) {    if (haystack[i].toLowerCase()
=== needle.toLowerCase()) {      return true;    }  }  return false; }
console.log(isIn([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn([‘a’,’b’,’c’], ‘d’));  // false

1
2
3
4
5
6
7
8
9
10
11
function isIn(haystack, needle) {
 for (var i = 0, len = haystack.length; i < len; i++) {
   if (haystack[i].toLowerCase() === needle.toLowerCase()) {
     return true;
   }
 }
 return false;
}
 
console.log(isIn([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn([‘a’,’b’,’c’], ‘d’));  // false

测试 JavaScript 函数的性能。今昔咱们来看哪个函数越来越快一些。大家得以独家运转每个函数14回,然后搜聚全数的度量结果:

JavaScript

function isIn1(haystack, needle) {  var found = false;
 haystack.forEach(function(element) {    if (element.toLowerCase() ===
needle.toLowerCase()) {      found = true;    }  });  return found; }
function isIn2(haystack, needle) {  for (var i = 0, len =
haystack.length; i < len; i++) {    if (haystack[i].toLowerCase()
=== needle.toLowerCase()) {      return true;    }  }  return false; }
console.log(isIn1([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn1([测试 JavaScript 函数的性能。’a’,’b’,’c’], ‘d’));  // false
console.log(isIn2([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn2([‘a’,’b’,’c’], ‘d’));  // false function
median(sequence) {  sequence.sort();  // note that direction doesn’t
matter  return sequence[Math.ceil(sequence.length / 2)]测试 JavaScript 函数的性能。; } function
measureFunction(func) {  var letters =
‘a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z’.split(‘,’);  var
numbers = [];  for (var i = 0; i < letters.length; i++) {    var t0
= performance.now();    func(letters, letters[i]);    var t1 =
performance.now();    numbers.push(t1 – t0);  }  console.log(func.name,
‘took’, median(numbers).toFixed(4)); } measureFunction(isIn1);
measureFunction(isIn2);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function isIn1(haystack, needle) {
 var found = false;
 haystack.forEach(function(element) {
   if (element.toLowerCase() === needle.toLowerCase()) {
     found = true;
   }
 });
 return found;
}
 
function isIn2(haystack, needle) {
 for (var i = 0, len = haystack.length; i < len; i++) {
   if (haystack[i].toLowerCase() === needle.toLowerCase()) {
     return true;
   }
 }
 return false;
}
 
console.log(isIn1([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn1([‘a’,’b’,’c’], ‘d’));  // false
console.log(isIn2([‘a’,’b’,’c’], ‘B’));  // true
console.log(isIn2([‘a’,’b’,’c’], ‘d’));  // false
 
function median(sequence) {
 sequence.sort();  // note that direction doesn’t matter
 return sequence[Math.ceil(sequence.length / 2)];
}
 
function measureFunction(func) {
 var letters = ‘a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z’.split(‘,’);
 var numbers = [];
 for (var i = 0; i < letters.length; i++) {
   var t0 = performance.now();
   func(letters, letters[i]);
   var t1 = performance.now();
   numbers.push(t1 – t0);
 }
 console.log(func.name, ‘took’, median(numbers).toFixed(4));
}
 
measureFunction(isIn1);
measureFunction(isIn2);

大家运维方面包车型大巴代码, 能够吸取如下的出口:

JavaScript

true false true false isIn1 took 0.0050 isIn2 took 0.0150

1
2
3
4
5
6
true
false
true
false
isIn1 took 0.0050
isIn2 took 0.0150

本条示例的在线演示如下所示:

究竟发生了哪些?第贰个函数的速度要快3倍!那不是大家假如的状态。

实则要是很轻便,可是有个别微妙。第二个函数使用了haystack.forEach方法,浏览器的JavaScript引擎会为它提供一些尾巴部分的优化,可是当大家使用数据索引工夫时,JavaScript引擎未有提供对应的优化。这告诉大家:在真正测量检验在此之前,你永久不会分晓。

结论

在大家试图解释怎么着接纳performance.now()方法取得JavaScript正确实施时间的进程中,大家临时发掘了叁个准绳场景,它的运作结果和我们的直觉相反。难题在于,要是您想要编写更加快的web应用,我们须要优化JavaScript代码。因为计算机(大概)是一个逼真的事物,它很难预测,偶然会拉动“欣喜”,所以只要领悟大家代码是还是不是运营更加快,最可信的主意就是编写制定测量检验代码并开始展览比较。

当大家有各样方法来做一件职业时,大家不了解哪一种格局运营更加快的另一个原因是要思念上下文。在上一节中,大家施行四个分寸写不灵动的字符串查询来查找1个字符串是还是不是在别的28个字符串中。当大家换贰个角度来相比1个字符串是或不是在其余100,000个字符串中时,结论恐怕是全然两样的。

地方的列表不是很完整的,因为还应该有越来越多的先天不足要求我们去开掘。举例,测量检验不现实的场景恐怕只在JavaScript引擎上测量检验。然而规定的是对此JavaScript开荒者来讲,倘使你想编写更加好越来越快的Web应用,performance.now()是一个很棒的主意。最终但不用最不重大,请谨记度量实行时间只是“越来越好的代码”的一反面。大家还要考虑内部存款和储蓄器消耗以及代码复杂度。

如何?你是还是不是早就选拔这么些函数来测验你的代码质量?若无,那您是怎么来测量试验性能的?请在底下的商议中享受你的主张,让大家开端谈论吗!

打赏帮忙本身翻译越来越多好小说,多谢!

打赏译者

测验 JavaScript 函数的脾性

2017/08/08 · JavaScript
· 函数,
时间

本文由 伯乐在线 –
Wing
翻译,周进林
校稿。未经许可,禁止转发!
法文出处:Peter
Bengtsson。应接加入翻译组。

在软件中,品质一贯扮演着首要的角色。在Web应用中,质量变得更为重视,因为一旦页面速度异常的慢的话,用户就能很轻易转去访谈大家的竞争对手的网址。作为正式的web开采人士,大家必供给思考那一个主题素材。有广大“古老”的关于品质优化的最好执行在明日依旧有效,举个例子最小化诉求数目,使用CDN以及不编写阻塞页面渲染的代码。可是,随着越来越多的web应用都在行使JavaScript,确定保障我们的代码运维的全速就变得很要紧。

若果你有二个正值职业的函数,然则你狐疑它运营得未有期望的那么快,何况你有贰个校勘它质量的布置。那怎么去注解那些只要呢?在明日,有啥样最好实践可以用来测量检验JavaScript函数的性质呢?一般的话,完毕那几个义务的一流艺术是利用内置的performance.now()函数,来衡量函数运维前和周转后的时刻。

在那篇小说中,我们商商量如何权衡代码运转时间,以及有啥样技巧能够制止有个别周围的“陷阱”。

缺陷 #3 – 太依仗平均值

在上一节中,大家上学到的七个很好的进行是再一次实施一些操作,理想状态下行使分裂的输入。但是,我们要牢记使用不相同的输入带来的标题,即某个输入的实践时间或然会花费全数别的输入的施行时间都长。那样让大家退一步来选取同样的输入。若是我们发送一样的输入十三回,每一遍都打字与印刷花费了多久。我们会获得像这么的出口:

JavaScript

Took 0.2730 milliseconds to generate: 77005292 Took 0.0234 milliseconds
to generate: 77005292 Took 0.0200 milliseconds to generate: 77005292
Took 0.0281 milliseconds to generate: 77005292 Took 0.0162 milliseconds
to generate: 77005292 Took 0.0245 milliseconds to generate: 77005292
Took 0.0677 milliseconds to generate: 77005292 Took 0.0289 milliseconds
to generate: 77005292 Took 0.0240 milliseconds to generate: 77005292
Took 0.0311 milliseconds to generate: 77005292

1
2
3
4
5
6
7
8
9
10
Took 0.2730 milliseconds to generate: 77005292
Took 0.0234 milliseconds to generate: 77005292
Took 0.0200 milliseconds to generate: 77005292
Took 0.0281 milliseconds to generate: 77005292
Took 0.0162 milliseconds to generate: 77005292
Took 0.0245 milliseconds to generate: 77005292
Took 0.0677 milliseconds to generate: 77005292
Took 0.0289 milliseconds to generate: 77005292
Took 0.0240 milliseconds to generate: 77005292
Took 0.0311 milliseconds to generate: 77005292

请小心第一遍时间和别的伍遍的时刻完全不雷同。那很也许是因为浏览器中的JavaScript引擎使用了优化措施,需求一些热身时间。大家基本上并没法制止这种情景,可是会有一点好的补救措施来阻拦我们得出有些谬误的定论。

一种办法是去总括前面9次的平分时间。其他一种尤其使用的主意是收集全数的结果,然后总括“中位数”。基本上,它会将具有的结果排列起来,对结果开始展览排序,然后取中间的一个值。那是performance.now()函数如此有用的地点,因为无论是你做什么样,你都能够赢得多少个数值。

让我们再试一回,此次我们选拔中位数函数:

JavaScript

var numbers = []; for (var i=0; i < 10; i++) {  var t0 =
performance.now();  makeHash(‘Peter’);  var t1 = performance.now();
 numbers.push(t1 – t0); } function median(sequence) {  sequence.sort();
 // note that direction doesn’t matter  return
sequence[Math.ceil(sequence.length / 2)]; } console.log(‘Median time’,
median(numbers).toFixed(4), ‘milliseconds’);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var numbers = [];
for (var i=0; i < 10; i++) {
 var t0 = performance.now();
 makeHash(‘Peter’);
 var t1 = performance.now();
 numbers.push(t1 – t0);
}
 
function median(sequence) {
 sequence.sort();  // note that direction doesn’t matter
 return sequence[Math.ceil(sequence.length / 2)];
}
 
console.log(‘Median time’, median(numbers).toFixed(4), ‘milliseconds’);

You may also like...

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图