您的位置:首页 > 其它

[转] 使用Duff's Device算法优化for循环

2011-02-14 10:25 148 查看
注意:
经过Aone的提醒发现一个问题,如果为了代码的可读性而将process()封装为函数,反而会导致增加了一次函数调用的指针跳转,拖慢了程序得不偿失。因此只推荐在需要极限优化超过代码可读性的情况下使用。


Duff's Device算法是一个老东西了,最早是在1983年C上由Tom Duff实现,然后2001年Jeff Greenberg移植到JavaScript上。算是很久的一个优化方案了 -_-b...竟然到现在才被发现。

话不多少,绕回正题,在遍历数组时众所周知的方法就是使用标准的for循环

var array:Array;//假设已有数据

标准方法:var length:int = array.length;

for(var i:int = 0; i < length; i++) {

process(array[i] as xxx);

}
复制代码一个基本的结构下来,除开process函数的代码质量不说,最大的开销放在 i < length 的Boolen比较之上,Duff's Device算法的目的即在于减少迭代的次数从而获得代码效率的提升。

Duff's Device实现一:var iterations:int = Math.floor(array.length / 8);

var startAt:int = array.length % 8;

var i:int = 0;

do {

swtich (startAt) {

case 0: process(array[i++]);

case 7: process(array[i++]);

case 6: process(array[i++]);

case 5: process(array[i++]);

case 4: process(array[i++]);

case 3: process(array[i++]);

case 2: process(array[i++]);

case 1: process(array[i++]);

}

startAt = 0;

} while(--iterations);
复制代码Duff's Device的基本理念是:在每次while循环中调用8次process函数,当然由于不是每个array都能被8整除,所以通过变量startAt记录下多出的余数,并通过switch在第一次循环时process多出来的部分。

通过这种巧妙的写法,迭代的次数被减少到了1/8左右。有人问那为什么是8,这里没有认真考究,但据说是跟内存的分配为8的倍数有关,每一次迭代进行8次process,有较大的可能分配到内存条相邻的的区间,从而提高跟寄存器的读取命中策略(Tom Duff当初貌似就是从汇编里取得灵感才写出最早那段C的代码),从而达到提高效率的目的。

Duff's Device优化实现(忘了经典实现吧,这个更好):var i:int = items.length % 8;

while(i) {

process(array[i--]);

}

i = Math.floor(array.length);

while(i) {

process(array[i--]);

process(array[i--]);

process(array[i--]);

process(array[i--]);

process(array[i--]);

process(array[i--]);

process(array[i--]);

process(array[i--]);

}
复制代码这里将非8的余数的部分单独抽离出来,减少了switch的判断,从而比经典循环更快。

效果:

在AS上做了测试,在100万次for,process函数为单步运算时,普通的for循环耗时40ms左右,优化版本的Duff's Device运行时间仅为4ms。可以预见在粒子动画和做游戏时遍历对象时和碰撞检测时应该会有很棒的效率提升。

当然不得不吐槽的是这种底层的优化不应该由这种“奇技淫巧”来实现,毕竟这样影响了代码的可读性。这部分的优化更应该在将for循环转化为机器码时做好处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: