hushc

buthcy

javascript减少循环迭代次数(”Dufff's Device算法“与“分时处理”)

<<高性能JavaScript>>中有一章提到对循环进行优化,策略之一就是减少循环迭代次数:

大家可能都听过C语言实现的“达夫设备”算法(Duff's Device),Jeff Greenberg被认为是将”达夫设备“代码从原始的C实现移植到JavaScript中的第一人,一个典型实现如下:

//为了方便调用,把它封装成一个函数

function duff(items) {
    var len = items.length, //缓存局部变量
        iterations = Math.floor(len / 8),  //商数,存放duff迭代次数
        startAt = len % 8,    //余数,存放duff一次迭代调用process的次数
        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--);    //书上是--iterations,貌似不对吧,应该是iterations--
};

Duff’s Device背后的基本理念是:每次循环中最多可调用8次process()。循环的迭代次数iterations为总数除以8,startAt用来存放余数,表示一次循环中应调用多少次process()。

假设我们定义process函数为:

function process(item) {

      alert(item);

};

调用一下duff函数:

duff([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);  //依次弹出1~12

//在这个调用过程中,我们可以知道iterations为1,startAt为4,也就是说,循环了两次,第一次循环调用process函数4次,第二次循环调用了8次。

可见,可以满足我们的遍历需求,而据书中介绍,如果迭代次数超过1000次,duff遍历算法的执行效率将明显提升。


据书中介绍,该算法有一个稍快的版本如下:(取消了switch语句,并将余数处理和主循环分开)

function duff2(items) {
    var len = items.length,
        i = len % 8;
    
    while(i) {
        process(items[i--]);
    };
    
    i = Math.floor(len / 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--]);
    }
};

<<高性能JavaScript>>这本书中是这样写这个算法的,不过仔细一看就知道,这个算法明显有问题,它无法遍历一个完整的数组,正确的算法应该是:

function duff2(items) {
    var len = items.length,

         t = 0,
        i = len % 8;
    
    while(i--) {
        process(items[t++]);
    };
    
    i = Math.floor(len / 8);
    while(i--){
      process(items[t++]);
      process(items[t++]);
      process(items[t++])
      process(items[t++]);
      process(items[t++]);
      process(items[t++]);
      process(items[t++]);
      process(items[t++]);
    }
};

这里有个老外写的Duff's device各种优化方法

这个算法对于处理数据量比较大的数组,还是很有执行效率的,比如对于处理省、市、区等级联下拉框的时候,如果你用数组来存放这些数据的话,就可以使用这个算法来遍历数组,可以感觉明显快很多。


还有一种优化数组遍历的方法,“分时处理”,这里有个不错的解析:

这个方法用着有点不放心(毕竟内部存在着定时器,搞不好数组还没遍历并更新完成,后面的代码都快执行完了,如果这个时候,后面的代码需要使用这个被遍历的数组,那更悲剧了,拿到的不一定是新数组),呵呵,视具体情况而定,比如说:

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];

//我现在想用分时处理的方式来遍历这个数组
chunk(a, function(v, i) { 
    a[i] = v + 1; // 假如我想在这里这个数组的每一项都+1
}, window);

//那么假如接下来,我想立即使用这个已经更新过的数组
console.log(a.toString());  //结果得到啥?

setTimeout(function() {
    console.log(a.toString()); //这里又是啥?
}, 100);


//未完待续,临时有事,晚上回去再继续~~~


先写到这里,不保证文章的正确性,仅供参考!

阅读更多
个人分类: web前端
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭