Migliorare le prestazioni di AS3 e Flex alleggerendo i calcoli sulla CPU
AS3, Flash, Flex | oscar | Commenta l'articolo |
- Togliere il controllo del gestore del mouse su tutti gli oggetti dove siamo sicuri che il mouse non sarà utilizzato.
SettiamomouseChildren = false; mouseEnabled = false;
- 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. - 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.
- 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. - 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;
- 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; }
- 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; }
- 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) { }
- Utilizzate i metodi asincroni piuttosto di quelli sincroni, come i metodi per l'accesso ai File o al DB.
-
E' buona norma rimuove l'evento una volta catturato, ovviamente dove non c'è più utilità dello stesso.
addEventListener e removeEventListener
-
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
- 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).
-
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 valorevar 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;
-
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 esempioimport 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:
- 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.
-
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.
- new Array() and new Object() sono tre volte più lenti di [] e {} ;
- non creare nuovi array o object, soprattutto all'interno di loop, al contrario cerca di aggiornarli;
- evita di fare lo splice di array molto lunghi, conviene settare l'indice dell'array a null;
- porre un oggetto a null è sempre meno impegnativo di cancellarlo, infatti sarà compito della garbage conllection di cancellarlo, migliorando le performance generali;
- non nidificare troppi loop, questo è molto dispendioso a livello di performance, al più porta la nidificazione a due;
- il passaggio per valore ad una funzione è meno performante rispetto al passaggio per riferimento;
Leggi anche l'articolo parte 2
Ultimi Commenti