Migliorare le prestazioni di AS3 e Flex alleggerendo i calcoli sulla CPU

AS3, Flash, Flex oscar Commenta l'articolo
  1. Togliere il controllo del gestore del mouse su tutti gli oggetti dove siamo sicuri che il mouse non sarà utilizzato.
    Settiamo
        mouseChildren = false;
        mouseEnabled = false; 
        
  2. quando Flash player perde il fuoco realizziamo una specie di "pause & resume" col seguente codice:
    var originalFrameRate:uint = stage.frameRate; 
    var standbyFrameRate:uint = 0; 
      
    stage.addEventListener ( Event.ACTIVATE, onActivate ); 
    stage.addEventListener ( Event.DEACTIVATE, onDeactivate ); 
      
    function onActivate ( e:Event ):void 
    { 
        // riprendi l'originale frame-rate 
        stage.frameRate = originalFrameRate; 
    } 
      
    function onDeactivate ( e:Event ):void 
    { 
        // setta il frame-rate a 0 
        stage.frameRate = standbyFrameRate; 
    }
      

    Gli eventi ACTIVATE e DEACTIVATE sono lanciati quando il flash player acquisisce o perde il fuoco.
    Potremmo eventualmente mettere il frame-rate a 4 e non a zero, in questo modo l’applicazione risulta apparentemente in pause, ma non effettivamente, infatti così non viene persa la connessiona al DB o al network, perché effettivamente l’applicazione continua a girare.

  3. Nel seguente codice, il file SWF è caricato e scaricato usando il metodo unload(), che richiede un maggiore processamento e un freezing manuale:<br>
    var loader:Loader = new Loader(); 
     
    loader.load ( new URLRequest ( "content.swf" ) ); 
     
    addChild ( loader ); 
     
    stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
     
    function unloadSWF ( e:MouseEvent ):void 
    { 
        // Unload the SWF file with no automatic object deactivation 
        // All deactivation must be processed manually 
        loader.unload(); 
    } 
      

    Una pratica migliore è l’uso del metoo unloadAndStop(), che gestisce il frezing nativo e forsa il processo di garbage collecting:

      var loader:Loader = new Loader(); 
     
    loader.load ( new URLRequest ( "content.swf" ) ); 
     
    addChild ( loader ); 
     
    stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
     
    function unloadSWF ( e:MouseEvent ):void 
    { 
        // Unload the SWF file with automatic object deactivation 
        // All deactivation is handled automatically 
        loader.unloadAndStop(); 
    }
      

    Le segunti azioni occorrono quando il metodo unloadAndStop() è chiamato:
    * I suoni sono stoppati.
    * Gli ascioltatori “Listners” degli eventi sul main della timeline sono fermati.
    * La classe Timer viene stoppata.
    * I dipositivi Hardware sono rilasciati (come camera e microfono).
    * Ogni MovieClip viene fermato.
    * Gli eventi Event.ENTER_FRAME, Event.FRAME_CONSTRUCTED, Event.EXIT_FRAME, Event.ACTIVATE and Event.DEACTIVATE sono fermati.

    Per conoscere il concetto del “Freezing e unfreezing” segui la lettura qui.

  4. Utilizzare il metodo setVector() piuttosto di setPixel() (o setPixel32()) migliore le prestazioni della CPU. Un ulteriore migliore è applicabile utilizzando i metodi lock() e unlock()
    prima di setPixel.
    Invece di ulilizzre
    getPixel (o getPixel32()) o getPixels (che permette di prelevare più pixel in un sol colpo) utilizzare getVector(), introdotto col Player 10.
    Quando vengono processati i pixel su una bitmap e non su un object list, qualche volta se la bitmap non referenzia un buffer la logica del lock e unlock non migliora le prestazioni.
  5. Invece di aggiungere del testo direttamente nella proprietà del textfield:
      for (var i:int = 0; i< 1500; i++ ) 
    { 
        myTextField.text += "ActionScript 3"; 
    } 
    
      

    Utilizzate il seguente codice:

      for (var i:int = 0; i< 1500; i++ ) 
    { 
        myTextField.appendText ( "ActionScript 3" ); 
    } 
    
      

    Oppure ancora meglio è utilizzare una variabile esterna:

     var content:String = myTextField.text; 
      
    for (var i:int = 0; i< 1500; i++ ) 
    { 
        content += "ActionScript 3"; 
    } 
      
    myTextField.text = content; 
    
      
  6. Invece di caricare un array in questo modo:
      for ( i = 0; i< lng; i++ ) 
    { 
        arraySprite[i] = new Sprite(); 
    } 
      
    for ( i = 0; i< lng; i++ ) 
    { 
        arraySprite[i].x = Math.random()*stage.stageWidth; 
        arraySprite[i].y = Math.random()*stage.stageHeight; 
        arraySprite[i].alpha = Math.random(); 
        arraySprite[i].rotation = Math.random()*360; 
    } 
    
      

    E' più performante fare così:

      for ( i = 0; i< lng; i++ ) 
    { 
        arraySprite[i] = new Sprite(); 
    } 
    
    var currentSprite:Sprite; 
      
    for ( i = 0; i< lng; i++ ) 
    { 
        currentSprite = arraySprite[i]; 
         currentSprite.x = Math.random()*stage.stageWidth; 
         currentSprite.y = Math.random()*stage.stageHeight; 
         currentSprite.alpha = Math.random(); 
         currentSprite.rotation = Math.random()*360; 
    } 
    
      
  7. Chiamare funnzioni all'interno di cicli è poco perfomante:
      for (i = 0; i< MAX_NUM; i++) 
    { 
        arrayValues[i] = Math.random()-Math.random(); 
    } 
      
    var currentValue:Number; 
      
    for (i = 0; i< MAX_NUM; i++) 
    { 
        currentValue = arrayValues[i]; 
        arrayValues[i] = Math.abs ( currentValue ); 
    } 
    
      

    dove possibile sostituiscila:

      for (i = 0; i< MAX_NUM; i++) 
    { 
        arrayValues[i] = Math.random()-Math.random(); 
    } 
      
    var currentValue:Number; 
      
    for (i = 0; i< MAX_NUM; i++) 
    { 
        currentValue = arrayValues[i]; 
        arrayValues[i] = currentValue > 0 ? currentValue : -currentValue; 
    } 
    
      
  8. Invece di usare il for così:
      for (var i:int = 0; i< myArray.length; i++) 
    { 
    }
      

    usalo così:

      var lng:int = myArray.length; 
     
    for (var i:int = 0; i< lng; i++) 
    { 
    }
      

    oppure meglio così:

      var i:int = myArray.length; 
     
    while (--i > -1) 
    { 
    }
      
  9. Utilizzate i metodi asincroni piuttosto di quelli sincroni, come i metodi per l'accesso ai File o al DB.
  10. E' buona norma rimuove l'evento una volta catturato, ovviamente dove non c'è più utilità dello stesso.

    addEventListener e removeEventListener
    
  11. Quando vengono usate le immagini non è sufficiente chiamare il metodo dispose per attivare la garbage collection, è necessario anche assegnare il valore null, come di seguito spiegato:

      
    // Create a BitmapData instance 
    var image:BitmapData = new BitmapData(800, 600); 
     
    trace(System.totalMemory / 1024); 
    // output: 44964 
     
    image.dispose(); 
    image = null; 
     
    trace(System.totalMemory / 1024); 
    // output: 43084

  12. Usa il nuovo text engine per i testi di sola lettura (per quelli che animi che non cambiano dinamicamente) e usa la classe TextField per i campi di testo di input (quelli che vengono usati per far inserire il testo dall'utente).
  13. wait_mc.addEventListener( Event.ENTER_FRAME, movePosition ); 
      
    // Switch to low quality 
    stage.quality = StageQuality.LOW; 
    var destX:int = stage.stageWidth >> 1; 
    var destY:int = stage.stageHeight >> 1; 
    var preloader:DisplayObject; 
    function movePosition ( e:Event ):void 
    { 
        preloader = e.currentTarget as DisplayObject; 
         
        preloader.x -= ( preloader.x - destX ) * .1; 
        preloader.y -= ( preloader.y - destY ) * .1; 
         
        if ( Math.abs ( preloader.y - destY ) < 1 ) 
        { 
            // Switch back to high quality 
            stage.quality = StageQuality.HIGH; 
            preloader.removeEventListener ( Event.ENTER_FRAME, movePosition ); 
        } 
    }

    Analizziamo il codice precedente che è stato ottimizzato:
    - metti in cache il campo TextField "wait_mc.cacheAsBitmap = true;";
    - utilizziamo "stage.quality = StageQuality.LOW;" per animare testo o sprite e solo quando l'animazione è finita riportiamo la qualità dello stage a HIGH;

    - utilizziamo "int" e non Number per arrotondare la posizione in modo da non dover usare funzioni come Math.round, Math.ceil;
    - per dividere un valore

    var destX:int = stage.stageWidth / 2;  
    var destY:int = stage.stageHeight / 2; 
    

    usa lo shift

    var destX:int = stage.stageWidth >> 1;  
    var destY:int = stage.stageHeight >> 1;
    
  14. metti in cache la bitmap quando hai un'immagine complessa di vettori, occhio però a bilanciare bene questa funzione, perché "cachare" una bitmap riduce i calcoli della CPU, ma aumenta l'utilizzo della memoria. In aggiunta se la bitmap cambia in dimensioni o ne viene cambiato il valore alpha la CPU viene utilizzata per ricreare la bitmap in cache. Se la bitmap cambia solo nella posizione x e y allora cachare è una caratteristica da sfruttare.
    E' possibile cachare anche da AS3 con l'istruzione "mySprite.cacheAsBitmap = true", questa soluzione è utile quando per esempio mySprite ha terminato la propria animazione o quando cambia solo nelle x o y, quindi tenerlo a "false" quando mySprite cambia di dimensione o nel fattore alpha. Per esempio

    import org.bytearray.bitmap.Sprite; 
     
    stage.addEventListener(MouseEvent.CLICK,createApples); 
    stage.addEventListener(KeyboardEvent.KEY_DOWN,cacheApples); 
     
    const MAX_NUM:int = 100; 
    var mySprite:Sprite; 
    var holder:Sprite = new Sprite(); 
     
    addChild(holder); 
     
    function createApples(e:MouseEvent):void 
    { 
        for (var i:int = 0; i< MAX_NUM; i++) 
        { 
            mySprite = new Sprite(); 
             
            holder.addChild(mySprite); 
        } 
    } 
     
    function cacheApples(e:KeyboardEvent):void 
    { 
        if (e.keyCode == 67) 
        { 
            var lng:int = holder.numChildren; 
             
            for (var i:int = 0; i < lng; i++) 
            { 
                mySprite = holder.getChildAt (i) as Sprite; 
                 
                mySprite.cacheAsBitmap = Boolean(!mySprite.cacheAsBitmap); 
            } 
        } 
    }
    

    E' importante cachare i singoli sprite e non un container, altrimenti saranno cachati più byte, infatti i pixel del container che raccolgono i singoli sprite sono memorizzati, anche se potrebbero essere risparmiati.
    Immagine con bitmap cache nel container:

    Immagine con bitmap cache nei singoli sprite:

  15. AIR (applicabile soprattutto per lo sviluppo dei dispositivi mobili) ha la caratteristica di utilizzare "DisplayObject.cacheAsBitmapMatrix" per cachare una bitmap e modificarne le dimensioni o il fattore alpha mantenendo il più possibile le prestazioni perfomanti.
  16. Per migliorare il rendering setta il giusto valore della qualità, soprattutto se sviluppi per dispositivi mobili:
    # StageQuality.LOW: permette le prestazioni migliori, toglie anche l'anti-aliasing.
    # StageQuality.MEDIUM: applica qualche anti-aliasing ma non realizza "l'arrotondamento" delle bitmaps.
    # StageQuality.HIGH: (Default per il desktop) Favorisce la riproduzione veloce e usa sempre l'anti-aliasing. Se il file SWF non contiene animazioni allora le bitmaps sono arrotondate; se il file SWF contiene animazioni allora le bitmaps non sono arrotondate.
    # StageQuality.BEST: fornisce la migliore qualità di render e non considera la velocità di riproduzione. Tutto viene realizzato con l'anti-aliased e le bitmaps sono sempre arrotondate.

    Usando StageQuality.MEDIUM spesso fornisce la sufficiente qualità per dispositivi mobili, e in qualche caso è sufficiente usare StageQuality.LOW per avere la sufficiente qualità. Da Flash Player 8, i testi con anti-aliased posso essere "renderizzati" accuratamente, spesso settando la qualità dello Stage a LOW.

  17. new Array() and new Object() sono tre volte più lenti di [] e {} ;
  18. non creare nuovi array o object, soprattutto all'interno di loop, al contrario cerca di aggiornarli;
  19. evita di fare lo splice di array molto lunghi, conviene settare l'indice dell'array a null;
  20. porre un oggetto a null è sempre meno impegnativo di cancellarlo, infatti sarà compito della garbage conllection di cancellarlo, migliorando le performance generali;
  21. non nidificare troppi loop, questo è molto dispendioso a livello di performance, al più porta la nidificazione a due;
  22. il passaggio per valore ad una funzione è meno performante rispetto al passaggio per riferimento;

Leggi anche l'articolo parte 2

Scrivi un Commento

Home | Graffiti e Disegni | Educazione | Chi siamo | Blog | Progetti | Contatti
RSS Feed Comments RSS Accedi