zoukankan      html  css  js  c++  java
  • 性能优化之算法和流程控制

    循环处理是最常见的编程模式之一,也是提升性能必须关注的要点之一。
     
    常见的优化方案有:
     
    ①JavaScript的四种循环(for、do-while、while、for-in)中,for-in循环比其他几种明显要慢。由于每次迭代操作会同时搜索实例或原型属性,for-in循环的每次迭代都会产生更多的开销,所以比其他类型要慢。因此遍历一个属性数量有限的已知属性列表,可以这样优化:
    var props = ['prop1', 'prop2'],i = 0;
    whlie(i < props.length){
       precess(object[props[i++]]);
    }
    该代码只关注给定的属性,减少了循环的开销。
    而对于,for、while、do-while。我在chrome下测试了一下,先创建一个大小为1000000的数组,每项乘100再叠加。
    测试用例:
    window.onload = function(){
    var items  = Array(1000000).join(',').split(',').map(function(item, index) {
       return index;
    });
       console.log(forCircle())
       console.log(whileCircle())
       console.log(doCircle())
    
    function forCircle(){
    console.profile();
    var currTime = new Date();
     var tal = 0; 
        for(var i = 0;i < items.length; i++){
           tal = tal + process(items[i]);
        }
       console.profileEnd();
       console.log('forCircle用时:' + (new Date() - currTime) + 'ms');
        return tal;
    }
    function whileCircle(){
    console.profile();
    var currTime = new Date();
        var tal = 0; 
        var j = 0;
        while (j < items.length){
          tal = tal + process(items[j++]);
        }
       console.profileEnd();
       console.log('whileCircle用时:' + (new Date() - currTime) + 'ms');
        return tal;
    }
    function doCircle(){
    console.profile();
    var currTime = new Date();
       var tal = 0; 
       var k = 0;
        do{
            tal = tal + process(items[k++]);
        }while (k < items.length) 
           console.profileEnd();
       console.log('doCircle用时:' + (new Date() - currTime) + 'ms');
        return tal;
    }
    function process(item){
        return item*100;
    }
    }
    

     取某次测试结果:

       

     

    平均来说,for循环耗时8ms,while耗时4ms,doWhile耗时也是4ms。for是最慢的。

     
    ②减少迭代的工作量。把数组长度保存在局部变量中再遍历、颠倒数组的遍历顺序。
    最常见的一个循环:
    for(var i = 0;i < items.length; i++){
       process(items[i]);
    }
    //
    var j = 0;
    while (j < items.length){
      process(items[j++]);
    }
    //
    var k = 0;
    do{
        process(items[k++]);
    }while (k < items.length)
    

    在这个循环中,每次运行都会产生如下操作:

    ①查找一次属性(items.length)

    ②执行数值比较一次(i < items.length) 

    ③查看控制条件是否为true(i < items.length ==true) 

    ④一次自增操作(i++)

    ⑤一次数组查找(items[i])

    ⑥一次函数调用(process(items[i]))

    若把数组长度存到一个局部变量,那么就不需要每次都查找一次items.length,也就提高了性能。

    改为这样:

    for(var i = 0, len = items.length;i < len; i++){
       process(items[i]);
    }
    //
    var j = 0,
    count = items.length;
    while (j < count){
      process(items[j++]);
    }
    //
    var k = 0,
    num = items.length;
    do{
        process(items[k++]);
    }while (k < num)
    

      这样在大多数浏览器中能节省大概25%的运行时间(IE中甚至可以节省50%)。总的来说,循环次数大的情况下,运行时间确实有提升。取某次结果如下:

     没有局部存量存储数组长度时:

     

     有局部变量存储数组长度时:

    ③减少迭代次数,“Duffs Device”即“达夫设备“循环体展开技术。适合于迭代次数较大的情况下。
    摘抄一下书中达夫设备的基本理念:每次循环中最多可 8 次调用 process()函数。循环迭代次数为元素总数除以8。 因为总数不一定是 8的整数倍, 所以 startAt 变量存放余数, 指出第一次循环中应当执行多少次 process()。比方说现在有 12 个元素,那么第一次循环将调用 process()4次,第二次循环调用 process()8 次,用 2 次循环代替了 12次循环。
    基本模式:
    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);  
    

      

    ④基于函数的迭代比基于循环的迭代消耗性能更多。例:for循环迭代与forEach函数迭代。
     
    ⑤优化if-else,通常来说,switch比if-else快,但是在判断条件较多时,使用查找表比if-else和switch都快。
     
    更多测试结论,可参考:http://www.cnblogs.com/hmking/archive/2011/10/10/2205321.html
    更多学习可参考《高性能JavaScript》
     
     
  • 相关阅读:
    记druid 在配置中心下的一个大坑: cpu 达到 100%
    常见java日志系统的搭配详解:关于slf4j log4j log4j2 logback jul jcl commons-logging jdk-logging
    HTML一片空白, 无法渲染: Empty tag doesn't work in some browsers
    再见:org.apache.catalina.connector.ClientAbortException: java.io.IOException: Connection reset by peer
    spring boot tomcat 打本地包成war,通过Tomcat启动时出现问题: ZipException: error in opening zip file
    Maven 错误:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project appservice-common: Fatal error compiling: 无效的目标发行版: 1.8
    Maven 错误 :The POM for com.xxx:jar:0.0.1-SNAPSHOT is invalid, transitive dependencies (if any) will not be available
    LocalVariableTable之 Slot 复用
    一些常见的Java面试题 & 面试感悟
    spring 2.5.6 错误:Context namespace element 'component-scan' and its parser class [org.springframework.context.annotation.ComponentScanBeanDefinitionParser] are only available on JDK 1.5 and higher
  • 原文地址:https://www.cnblogs.com/LuckyWinty/p/6381970.html
Copyright © 2011-2022 走看看