读高性能JavaScript编程 第四章 Duff's Device

时间:2022-05-28 21:33:44

  又要开始罗里吧嗦的 第四章  Summary 了。

  这一次我尽量精简语言。

  如果你认为 重复调用一个方法数次有点辣眼睛的话 比如:

function test(i){
  process(i++);
  process(i++);
  process(i++);
  process(i++);
}

那么你可能选择更优解 比如:

function test(i){
for(j=0,j<4,j++){
  process(i++);
}
}

但其实第一种性能优于第二种。

有人可能已经想到  Duff's Device 了,没错 这一章就是讲算法和流程控制。

文章首先解释的是循环:

      四种循环

  1. for         :初始化体,前测条件,后执行体,循环体
  2. while  :预测试条件,循环体
  3. do-while :循环体,后测试条件体
  4. for-in  :枚举任何对象的命名属性(字符串,包括从原型链继承来的属性)

  两个因素

  1. 每次迭代干什么
  2. 迭代的次数

举个例子:

情况1、如果你的循环体 没有任何东西,那么循环100次等于一次不循环。 这是循环的性能等于迭代的次数了。

情况2、现在加一个条件,你的循环体里只做了一件小事 比如声明一个变量。 那么现在迭代的次数对循环的性能依旧影响深远。

情况3、如果循环是500000 次。那么迭代最好只干不得不干的事,迭代的次数对性能的影响很大。

  如何减少迭代次数?

用局部变量来充当缓存的事前几章已经没少讲,所以第一种因素 每次迭代做的事 就不提了。倒序循环可以略微提高循环性能,这是一种典型的分析循环中做的所有事,并精简它的例子。

for (var i=items.length; i--; ){
process(items[i]);
}

重点是如何减少迭代次数。直接上代码:

 //credit: Jeff Greenberg
var iterations = Math.floor(items.length / 8),
startAt = items.length % 8,
i = 0;
do {
switch(startAt){
case 0: process(items[i++]);
case 7: process(items[i++]);
case 6: process(items[i++]);
case 5: process(items[i++]);
case 4: process(items[i++]);
case 3: process(items[i++]);
case 2: process(items[i++]);
case 1: process(items[i++]);
}
startAt = 0;
} while (--iterations);

这个方法反应了一种思想,循环处理的原因应该是不确定目标的数量或目标数量太大而不得不循环,不是仅仅为了处理每一项而循环。

如果我需要处理一个已知count为10的集合项 我大可以直接调用10次。

还有一个稍快的版本:

 //credit: Jeff Greenberg
var i = items.length % 8;
while(i){
process(items[i--]);
}
i = Math.floor(items.length / 8);
while(i){
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
}

这样做能大大减少循环次数,而效果就是 500'000 次迭代中,运行时间比普通循环减少到 70%。但是这不应该仅仅适用于此,这应该是一种思想而不是告诉你这么做会更快。

类似的可以看尾递归和递归的故事。