Minimizing CPU usage

Another important area of focus for optimization is CPU usage. Optimizing CPU processing improves performance, and as a result, battery life on mobile devices.

Flash Player 10.1 enhancements for CPU usage

Flash Player 10.1 introduces two new features that help save CPU processing. The features involve pausing and resuming SWF content when it goes offscreen, and limiting the number of Flash Player instances on a page.

Pause, throttle, and resume

Note: The pause, throttle, and resume feature does not apply to Adobe® AIR® applications.

To optimize CPU and battery usage, Flash Player 10.1 introduces a new feature on related to inactive instances. This feature allows you to limit CPU usage by pausing and resuming the SWF file when content goes off and on the screen. With this feature, Flash Player releases as much memory as possible by removing any objects that can be recreated when the playing of content is resumed. Content is considered offscreen when the entire content is offscreen.

Two scenarios cause the SWF content to be offscreen:

  • The user scrolls the page and causes the SWF content to move offscreen.

    In this case, if there is any audio or video playback, content continues to play, but rendering is stopped. If there is no audio or video playing, to ensure that the playback or ActionScript execution is not paused, set the hasPriority HTML parameter to true. However, keep in mind that SWF content rendering is paused when content is offscreen or hidden, regardless of the value of the hasPriority HTML parameter.

  • A tab is opened in the browser, which causes the SWF content to move to the background.

    In this case, regardless of the value of the hasPriority HTML tag, the SWF content is slowed down, or throttled , to between 2 and 8 fps. Audio and video playback is stopped and no content rendering is processed unless the SWF content becomes visible again.

For Flash Player 11.2 and later running on Windows and Mac desktop browsers, you can use the ThrottleEvent in your application. Flash Player dispatches a ThrottleEvent when Flash Player pauses, throttles, or resumes play.

The ThrottleEvent is a broadcast event, which means that it is dispatched by all EventDispatcher objects with a listener registered for this event. For more information about broadcast events, see the DisplayObject class.

Instance management

Note: The instance management feature does not apply to Adobe® AIR® applications.
Use the hasPriority HTML parameter to delay loading of offscreen SWF files.

Flash Player 10.1 introduces a new HTML parameter called hasPriority :

<param name="hasPriority" value="true" />

This feature limits the number of Flash Player instances that are started on a page. Limiting the number of instances helps conserve CPU and battery resources. The idea is to assign a specific priority to SWF content, giving some content priority over other content on a page. Consider a simple example: a user is browsing a website and the index page hosts three different SWF files. One of them is visible, another one is partially visible onscreen, and the last one is offscreen, requiring scrolling. The first two animations are started normally, but the last one is deferred until it becomes visible. This scenario is the default behavior when the hasPriority parameter is not present or set to false . To ensure that a SWF file is started, even if it is offscreen, set the hasPriority parameter to true . However, regardless of the value of the hasPriority parameter, a SWF file that is not visible to the user always has its rendering paused.

Note: If available CPU resources become low, Flash Player instances are no longer started automatically, even if the hasPriority parameter is set to true . If new instances are created through JavaScript after the page has been loaded, those instances will ignore the hasPriority flag. Any 1x1 pixel or 0x0 pixel content is started, preventing helper SWF files from being deferred if the webmaster fails to include the hasPriority flag. SWF files can still be started when clicked, however. This behavior is called “click to play.”

The following diagrams show the effects of setting the hasPriority parameter to different values:

Effects of different values for the hasPriority parameter

Effects of different values for the hasPriority parameter

Sleep mode

Flash Player 10.1 and AIR 2.5 introduce a new feature on mobile devices that helps save CPU processing, and as a result, battery life. This feature involves the backlight found on many mobile devices. For example, if a user running a mobile application is interrupted and stops using the device, the runtime detects when the backlight goes into sleep mode. It then drops the frame rate to 4 frames per second (fps), and pauses rendering. For AIR applications, sleep mode also begins when the application moves to the background.

ActionScript code continues to execute in sleep mode, similar to setting the Stage.frameRate property to 4 fps. But the rendering step is skipped, so the user cannot see that the player is running at 4 fps. A frame rate of 4 fps was chosen, rather than zero, because it allows all the connections to remain open (NetStream, Socket, and NetConnection). Switching to zero would break open connections. A 250 ms refresh rate was chosen (4 fps) because many device manufacturers use this frame rate as their refresh rate. Using this value keeps the frame rate of the runtime in the same ballpark as the device itself.

Note: When the runtime is in sleep mode, the Stage.frameRate property returns the frame rate of the original SWF file, rather than 4 fps.

When the backlight goes back into on mode, rendering is resumed. The frame rate returns to its original value. Consider a media player application in which a user is playing music. If the screen goes into sleep mode, the runtime responds based on the type of content being played. Here is a list of situations with the corresponding runtime behavior:

  • The backlight goes into sleep mode and non-A/V content is playing: The rendering is paused and the frame rate is set to 4 fps.

  • The backlight goes into sleep mode and A/V content is playing: the runtime forces the backlight to be always on, continuing the user experience.

  • The backlight goes from sleep mode to on mode: the runtime sets the frame rate to the original SWF file frame rate setting and resumes rendering.

  • Flash Player is paused while A/V content is played: Flash Player resets the backlight state to the default system behavior because A/V is no longer playing.

  • Mobile device receives a phone call while A/V content is played: The rendering is paused and the frame rate is set to 4 fps.

  • The backlight sleep mode is disabled on a mobile device: the runtime behaves normally.

When the backlight goes into sleep mode, rendering pauses and the frame rate slows down. This feature saves CPU processing, but it cannot be relied upon on to create a real pause, as in a game application.

Note: No ActionScript event is dispatched when the runtime enters or leaves sleep mode.

Freezing and unfreezing objects

Freeze and unfreeze objects properly by using the REMOVED_FROM_STAGE and ADDED_TO_STAGE events.

To optimize your code, always freeze and unfreeze your objects. Freezing and unfreezing are important for all objects, but are especially important for display objects. Even if display objects are no longer in the display list and are waiting to be garbage collected, they could still be using CPU-intensive code. For example, they can still be using Event.ENTER_FRAME. As a result, it is critical to freeze and unfreeze objects properly with the Event.REMOVED_FROM_STAGE and Event.ADDED_TO_STAGE events. The following example shows a movie clip playing on stage that interacts with the keyboard:

// Listen to keyboard events 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyIsDown); 
stage.addEventListener(KeyboardEvent.KEY_UP, keyIsUp); 
  
// Create object to store key states 
var keys:Dictionary = new Dictionary(true); 
  
function keyIsDown(e:KeyboardEvent):void 
{ 
    // Remember that the key was pressed 
    keys[e.keyCode] = true;     
  
    if (e.keyCode==Keyboard.LEFT || e.keyCode==Keyboard.RIGHT) 
    { 
        runningBoy.play(); 
    } 
} 
  
function keyIsUp(e:KeyboardEvent):void 
{ 
    // Remember that the key was released 
    keys[e.keyCode] = false; 
  
    for each (var value:Boolean in keys) 
          if ( value ) return; 
    runningBoy.stop(); 
} 
  
runningBoy.addEventListener(Event.ENTER_FRAME, handleMovement); 
runningBoy.stop(); 
  
var currentState:Number = runningBoy.scaleX; 
var speed:Number = 15; 
  
function handleMovement(e:Event):void 
{ 
    if (keys[Keyboard.RIGHT]) 
    { 
        e.currentTarget.x += speed; 
        e.currentTarget.scaleX = currentState;     
    } else if (keys[Keyboard.LEFT]) 
    { 
        e.currentTarget.x -= speed; 
        e.currentTarget.scaleX = -currentState; 
    } 
}

View full size graphic
Movie clip that interacts with keyboard

When the Remove button is clicked, the movie clip is removed from the display list:

// Show or remove running boy 
showBtn.addEventListener (MouseEvent.CLICK,showIt); 
removeBtn.addEventListener (MouseEvent.CLICK,removeIt); 
 
function showIt (e:MouseEvent):void 
{ 
    addChild (runningBoy); 
} 
 
function removeIt(e:MouseEvent):void 
{ 
    if (contains(runningBoy)) removeChild(runningBoy); 
}

Even when removed from the display list, the movie clip still dispatches the Event.ENTER_FRAME event. The movie clip still runs, but it is not rendered. To handle this situation correctly, listen to the proper events and remove event listeners, to prevent CPU-intensive code from being executed:

// Listen to Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE 
runningBoy.addEventListener(Event.ADDED_TO_STAGE,activate); 
runningBoy.addEventListener(Event.REMOVED_FROM_STAGE,deactivate); 
 
function activate(e:Event):void 
{ 
    // Restart everything 
    e.currentTarget.addEventListener(Event.ENTER_FRAME,handleMovement); 
} 
 
function deactivate(e:Event):void 
{ 
    // Freeze the running boy - consumes fewer CPU resources when not shown 
    e.currentTarget.removeEventListener(Event.ENTER_FRAME,handleMovement); 
    e.currentTarget.stop(); 
}

When the Show button is pressed, the movie clip is restarted, it listens to Event.ENTER_FRAME events again, and the keyboard correctly controls the movie clip.

Note: If a display object is removed from the display list, setting its reference to null after removing it does not ensure that the object is frozen. If the garbage collector doesn’t run, the object continues to consume memory and CPU processing, even though the object is no longer displayed. To make sure that the object consumes the least CPU processing possible, make sure that you completely freeze it when removing it from the display list.

Starting with Flash Player 10 and AIR 1.5, the following behavior also occurs. If the playhead encounters an empty frame, the display object is automatically frozen even if you did not implement any freezing behavior.

The concept of freezing is also important when loading remote content with the Loader class. When using the Loader class with Flash Player 9 and AIR 1.0, it was necessary to manually freeze content by listening to the Event.UNLOAD event dispatched by the LoaderInfo object. Every object had to be manually frozen, which was a non-trivial task. Flash Player 10 and AIR 1.5 introduced an important new method on the Loader class called unloadAndStop() . This method allows you to unload a SWF file, automatically freeze every object in the loaded SWF file, and force the garbage collector to run.

In the following code, the SWF file is loaded and then unloaded using the unload() method, which requires more processing and manual freezing:

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(); 
}

A best practice is to use the unloadAndStop() method, which handles the freezing natively and forces the garbage collecting process to run:

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(); 
}

The following actions occur when the unloadAndStop() method is called:

  • Sounds are stopped.

  • Listeners registered to the SWF file’s main timeline are removed.

  • Timer objects are stopped.

  • Hardware peripheral devices (such as camera and microphone) are released.

  • Every movie clip is stopped.

  • Dispatching of Event.ENTER_FRAME , Event.FRAME_CONSTRUCTED , Event.EXIT_FRAME , Event.ACTIVATE and Event.DEACTIVATE is stopped.

Activate and deactivate events

Use Event.ACTIVATE and Event.DEACTIVATE events to detect background inactivity and optimize your application appropriately.

Two events ( Event.ACTIVATE and Event.DEACTIVATE ) can assist you in fine-tuning your application so that it uses the fewest CPU cycles possible. These events allow you to detect when the runtime gains or loses focus. As a result, code can be optimized to react to context changes. The following code listens to both events and dynamically changes the frame rate to zero when the application loses its focus. For example, the animation can lose focus when the user switches to another tab or puts the application into the background:

var originalFrameRate:uint = stage.frameRate; 
var standbyFrameRate:uint = 0; 
  
stage.addEventListener ( Event.ACTIVATE, onActivate ); 
stage.addEventListener ( Event.DEACTIVATE, onDeactivate ); 
  
function onActivate ( e:Event ):void 
{ 
    // restore original frame rate 
    stage.frameRate = originalFrameRate; 
} 
  
function onDeactivate ( e:Event ):void 
{ 
    // set frame rate to 0 
    stage.frameRate = standbyFrameRate; 
}

When the application gains focus again, the frame rate is reset to its original value. Instead of changing the frame rate dynamically, you could also consider making other optimizations, such as freezing and unfreezing objects.

The activate and deactivate events allow you to implement a similar mechanism to the "Pause and Resume" feature sometimes found on mobile devices and Netbooks.

Mouse interactions

Consider disabling mouse interactions, when possible.

When using an interactive object, such as a MovieClip or Sprite object, the runtime executes native code to detect and handle mouse interactions. Detecting mouse interaction can be CPU-intensive when many interactive objects are shown onscreen, especially if they overlap. An easy way to avoid this processing is to disable mouse interactions on objects that do not require any mouse interaction. The following code illustrates the use of the mouseEnabled and mouseChildren properties:

// Disable any mouse interaction with this InteractiveObject 
myInteractiveObject.mouseEnabled = false; 
const MAX_NUM:int = 10; 
  
// Create a container for the InteractiveObjects 
var container:Sprite = new Sprite(); 
  
for ( var i:int = 0; i< MAX_NUM; i++ ) 
{ 
    // Add InteractiveObject to the container 
    container.addChild( new Sprite() ); 
} 
  
// Disable any mouse interaction on all the children 
container.mouseChildren = false;

When possible, consider disabling mouse interaction, which helps your application to use less CPU processing, and as a result, reduce battery usage on mobile devices.

Timers versus ENTER_FRAME events

Choose either timers or ENTER_FRAME events, depending on whether content is animated.

Timers are preferred over Event.ENTER_FRAME events for non-animated content that executes for a long time.

In ActionScript 3.0, there are two ways of calling a function at specific intervals. The first approach is to use the Event.ENTER_FRAME event dispatched by display objects (DisplayObject). The second approach is to use a timer. ActionScript developers frequently use the ENTER_FRAME event approach. The ENTER_FRAME event is dispatched on every frame. As a result, the interval at which the function is called is related to the current frame rate. The frame rate is accessible through the Stage.frameRate property. However, in some cases, using a timer can be a better choice than using the ENTER_FRAME event. For example, if you don’t use animation, but would like your code called at specific intervals, using a timer can be a better choice.

A timer can behave in a similar way to an ENTER_FRAME event, but an event can be dispatched without being tied to the frame rate. This behavior can offer some significant optimization. Consider a video player application as an example. In this case, you do not need to use a high frame rate, because only the application controls are moving.

Note: The frame rate does not affect the video, because the video is not embedded in the timeline. Instead, the video is loaded dynamically through progressive downloading or streaming.

In this example, the frame rate is set to a low value of 10 fps. The timer updates the controls at a rate of one update per second. The higher update rate is made possible by the updateAfterEvent() method, which is available on the TimerEvent object. This method forces the screen to be updated each time the timer dispatches an event, if needed. The following code illustrates the idea:

// Use a low frame rate for the application 
stage.frameRate = 10; 
  
// Choose one update per second 
var updateInterval:int = 1000; 
var myTimer:Timer = new Timer(updateInterval,0); 
  
myTimer.start(); 
myTimer.addEventListener( TimerEvent.TIMER, updateControls ); 
  
function updateControls( e:TimerEvent ):void 
{ 
    // Update controls here 
    // Force the controls to be updated on screen 
    e.updateAfterEvent(); 
}

Calling the updateAfterEvent() method does not modify the frame rate. It just forces the runtime to update the content onscreen that has changed. The timeline still runs at 10 fps. Remember that timers and ENTER_FRAME events are not perfectly accurate on low performance devices, or if event handler functions contain code that requires heavy processing. Just like the SWF file frame rate, the update frame rate of the timer can vary in some situations.

Minimize the number of Timer objects and registered enterFrame handlers in your application.

Each frame, the runtime dispatches an enterFrame event to each display object in its display list. Although you can register listeners for the enterFrame event with multiple display objects, doing so means that more code is executed each frame. Instead, consider using a single centralized enterFrame handler that executes all the code that is to run each frame. By centralizing this code, it is easier to manage all the code that is running frequently.

Likewise, if you’re using Timer objects, there is overhead associated with creating and dispatching events from multiple Timer objects. If you must trigger different operations at different intervals, here are some suggested alternatives:

  • Use a minimal number of Timer objects and group operations according to how frequently they happen.

    For example, use one Timer for frequent operations, set to trigger every 100 milliseconds. Use another Timer for less-frequent or background operations, set to trigger every 2000 milliseconds.

  • Use a single Timer object, and have operations triggered at multiples of the Timer object’s delay property interval.

    For example, suppose you have some operations that are expected to happen every 100 milliseconds, and others that you want to happen every 200 milliseconds. In that case, use a single Timer object with a delay value of 100 milliseconds. In the timer event handler, add a conditional statement that only runs the 200-millisecond operations every other time. The following example demonstrates this technique:

    var timer:Timer = new Timer(100); 
    timer.addEventListener(TimerEvent.Timer, timerHandler); 
    timer.start(); 
         
    var offCycle:Boolean = true; 
      
    function timerHandler(event:TimerEvent):void 
    { 
        // Do things that happen every 100 ms 
         
        if (!offCycle) 
        { 
            // Do things that happen every 200 ms 
        } 
         
        offCycle = !offCycle; 
    }
Stop Timer objects when not in use.

If a Timer object’s timer event handler only performs operations under certain conditions, call the Timer object’s stop() method when none of the conditions are true.

In enterFrame event or Timer handlers, minimize the number of changes to the appearance of display objects that cause the screen to be redrawn.

Each frame, the rendering phase redraws the portion of the stage that has changed during that frame. If the redraw region is large, or if it’s small but contain a large quantity or complex display objects, the runtime needs more time for rendering. To test the amount of redrawing required, use the “show redraw regions” feature in the debug Flash Player or AIR.

For more information about improving performance for repeated actions, see the following article:

Tweening syndrome

To save CPU power, limit the use of tweening, which saves CPU processing, memory, and battery life.

Designers and developers producing content for Flash on the desktop tend to use many motion tweens in their applications. When producing content for mobile devices with low performance, try to minimize the use of motion tweens. Limiting their use helps content run faster on low-tier devices.

// Ethnio survey code removed