首先我们先要了解循环展开(Loop unwinding)
for (i = 1; i <= 60; i++){
a[i] = a[i] * b + c;
}
可以如此循环展开:
for (i = 1; i <= 60; i+=3){
a[i] = a[i] * b + c;
a[i+1] = a[i+1] * b + c;
a[i+2] = a[i+2] * b + c;
}
这被称为展开了两次。虽然看起来程序优化之后比以前还长了,但是循环的次数却减少了,同时循环中的条件判断(i <= 60)也相应减少了。
但很明显,如果总循环次数无法被循环增量整除时会有剩余,所以就有了达夫设备。
let testVal = 0;
let n = iterations / 8;
let caseTest = iterations % 8;
do {
switch (caseTest) {
case 0:
testVal++;
case 7:
testVal++;
case 6:
testVal++;
case 5:
testVal++;
case 4:
testVal++;
case 3:
testVal++;
case 2:
testVal++;
case 1:
testVal++;
}
caseTest = 0;
}
while (--n > 0);
若iterations
无法为8整除,则仍有iterations%8
项未处理;所以加入了switch/case
语句,但这里没有使用break
关键字,我们通过fall through
特性来处理剩余数据(注:C#强制break,golang默认break)。
Duff’s device
这是在jsperf
上的测试,可以看到循环展开是普通循环的三倍,不过要注意然各个版本的浏览器也存在一定差异。
我认为在前端开发中,大多数情况下没有必要进行这种优化的,除非数据量很大,或者你在写的是组件库,并且做过测试证明真的需要它。