即使是循环中最快的代码,累计迭代上千次也会慢下来。此外,循环体运行时也会带来小性能开销,不仅仅是增加了总体运行时间。减少迭代次数能获得更加显著的性能提升,最广为人知的一种限制循环迭代次数的模式被称为“达夫设备(Duff's Device)”。
Duff's Device是一种循环体展开技术,它使得一次迭代中实际执行了多次迭代的操作。一个典型的实现如下:
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);
达夫设备背后的基本理念是:每次循环中最多可8次调用process()函数。循环迭代次数为元素总数除以8.因为总数不一定是8的整数倍,所以startAt变量存放余数,指出第一次循环中应当执行多少次process()。比方说现在有12个元素,那么第一次循环将调用process()4次,第二次循环调用process()8次,用2次循环代替了12次循环。
次算法一个稍快的版本取消了switch表达式,将余数处理与主循环分开:
<script type="text/javascript">
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--]);
}
</script>
尽管这种实现方式用两个循环替代了之前得一次循环,但它移除了了循环体中的switch语句,速度比原始循环更快。
是否应该使用达夫设备,无论是原始的版本还是修改后的版本,很大程度上依赖于迭代的次数。如果循环迭代次数少于1000次,你可能只看到它与普通循环相比只有微不足道的性能提升。如果迭代次数超过1000次,达夫设备的效率明显提升。例如500000次迭代中,运行时间比普通循环减少到70%。