21 January 2013
Adobe Scout is an incredibly powerful tool for optimizing your Flash content, because it lets you see what's really going on behind the scenes in Flash Player. But having this information is most useful if you understand why Flash Player does what it does. Only then can you efficiently figure out how to fix the problems that Scout tells you about!
The purpose of this article is to give you a basic understanding of how Flash Player works, and to relate this to the information you see in Scout. It's also a reference guide to the terminology used by Scout, so that you can look up the meaning of the different activities performed by Flash Player. If you've used Scout and thought "I can see I'm spending too much time doing x, but what does x mean?", then this article is for you!
Note that this isn't a guide to the user interface of Scout. If you don't know how to use Scout, or what the different panels do, then you should begin by reading the getting started guide.
Flash Player is like a personal assistant in that it arranges complicated stuff on your behalf, and acts as an interface between you and the real world. But Flash Player can't do anything useful by itself–it needs you to tell it what to do! There are two ways of doing this:
When Flash Player executes your scripts, or the tags in your timeline, they tell it to perform various activities. These can be roughly classified into four types:
It's important to keep this in mind as you're reading this article. Flash Player isn't just about executing ActionScript; it does many other activities too! If you think of Flash Player as your personal assistant, then executing ActionScript is like being in a meeting. Even if it's a really short meeting, Flash Player still has to go off afterwards and do all the paperwork you gave it. Rendering your fancy animation takes time, and much of the actual work happens outside of your ActionScript!
You may be wondering how Flash Player manages the execution of all these different activities.
When you run Flash content in a web browser, Flash Player typically runs in a separate operating system process. There's only one process for all the SWFs running in that browser, including any related worker threads, and Flash Player manages the execution of these SWFs so that it looks like they're running independently. Each running SWF is known as a player instance, and this corresponds to a session in Scout. Worker threads are player instances too. For AIR content, there's only one main player instance, plus any workers it uses.
As Flash Player runs, it switches between different activities. It might do a little script execution, then some video playback, then render some graphics, and so on. The exact mix depends on what events are happening, what ongoing activities are in progress, and what activities have been requested by the SWF's scripts and timelines. While all of this is going on, Flash Player keeps a record of what it does, and measures the resources it consumes, including CPU time, CPU memory, and GPU memory. These measurements are sent to Scout, so that you can see what's going on. To keep the overhead of these measurements to a minimum, Flash Player only measures and reports activities that take a significant amount of time or memory.
At the most basic level, Flash Player can be in one of two modes at any moment in time:
In Scout, the total time (the sum of active and inactive time) is indicated by the grey bars in the Frame Timeline. To make it easier to visualize what proportion of time Flash Player spends doing different types of activity, Scout breaks down the active time into four top-level categories:
It's important to understand that Scout shows you elapsed time, and not CPU time. Think of Flash Player as sitting there with a stopwatch, timing each activity. If the operating system interrupts Flash Player in the middle of the activity, and lets another application run for a while, then Flash Player will measure a longer elapsed time. To get the most accurate data, you should close other applications while you're profiling with Scout.
Many Flash applications, especially games, do a great deal of graphical animation. The basic principle is to redraw the screen at a certain frequency—for example 60 times per second—so it looks like a smooth animation. To help you with this style of programming, Flash Player has a core concept of frames. Deep inside the player, there's a beating heart—a timer that fires at a certain frequency called the framerate. The time in between these heartbeats is called a frame. You get to decide the framerate you want your content to run at. It's a property of your SWF, and you can dynamically change it from ActionScript while the SWF is running.
A common way of programming is to register an event handler for
Event.ENTER_FRAME, the event that corresponds to the heartbeat and the beginning of a new frame. If you're writing a game, you can use this event handler to do all the periodic work needed to keep the game running. For example, you can process any inputs from the user, update the game state using your physics engine, and finally render the updated scene.
When you're profiling with Scout, the Frame Timeline shows you how long Flash Player spent executing each frame. Consider a game for which you want to achieve a framerate of 60 frames per second (60fps). There are 1000 milliseconds (ms) in a second, so you have a budget of 1000/60 = 16.7ms per frame. If, on average, each frame takes longer than this, Flash Player won't be able to run at the framerate you requested, and your content might look slow and jittery. In Scout's Frame Timeline, the budget is shown as a horizontal red line, and the grey bars represent the total time taken by each frame.
Within a frame, Flash Player has many different activities to perform. One of the first things it does is to dispatch an
Event.ENTER_FRAME event and call any registered event handlers. As it executes your ActionScript code, this might cause new deferred or ongoing activities to get added to the "todo list", like redrawing part of the screen or starting a new file download. These have to get executed sometime later on during the frame. Also, mouse, keyboard, and other events might happen during the frame, and these need to get processed and handled. Eventually, Flash Player will finish all its tasks for the frame and have nothing left to do. It then sits and waits for the next heartbeat, so it can start all over again with a new frame!
In Scout, the main thing to watch out for is a colored bar that crosses the red line. This means that Flash Player took longer than the entire frame's budget to do everything it needed to do. In other words, Flash Player had too much work to do in its allotted time! If all the frames consistently go over the budget, then your content will generally slow down (see Figure 1), whereas if you get lots of erratic spikes, your content will be jerky.
Note that even if you finish the frame in good time, Flash Player will always wait before starting the next frame. This is a good thing—it means that your content isn't hogging all the resources on the device, which reduces power consumption and gives the CPU time to carry out other tasks. It also means that your content has slack built into it, so that it can still run at the target framerate on a slower device. It's completely normal for the grey bars to hover around the red line (see Figure 2).
There are a few subtle things about frames to be aware of:
Now that you have a better understanding of the basic structure of Flash Player, it's time to dive a bit deeper into the data that you see in Scout. You'll notice that the Top Activities and Activity Sequence panels give a detailed breakdown of what Flash Player is doing. The descriptions in these panels are designed to be as self-explanatory as possible, but sometimes you need a bit more information to get to the root of the problem. This section groups the different activities of Flash Player into functional areas (see Figure 4), and provides a more detailed explanation of what they mean.
You can use this section as a reference guide when using Scout, and just read the parts that are relevant to your problem. Alternatively, you can read the entire section to learn a bit more about how the different parts of Flash Player work.
ActionScript is the language of Flash; it lets you tell Flash Player what to do. When you build a SWF, your ActionScript code gets compiled into a lower-level language called ActionScript bytecode that Flash Player can understand. This bytecode then gets executed by the ActionScript Virtual Machine (AVM), while your SWF is running. The AVM implements certain core features of ActionScript, including garbage collection and exceptions, and also acts as a bridge between your code and Flash Player.
You can interact with Flash Player from ActionScript using APIs. To you these look like normal function calls, but under the hood you're often calling native C code, where all the magic happens! In Scout, these APIs show up as normal function calls in the ActionScript panel, so you don't have to worry about the internal details. If you're spending too long in an API call, you should either call it less often or give it less work to do (by reducing the size or complexity of the arguments).
As well as invoking Flash Player from ActionScript, you need to tell Flash Player when to execute your code. Most of the time, this is done using event handlers. These are functions that you register to get called when a specific event happens. You can create custom events, and call them manually using
EventDispatcher.dispatch(), but you'll often want to listen for external events, like mouse movements and key presses. There are a number of activities in Scout that relate to this:
Timers are handled much like external events, except that you set them up yourself:
TimerEvent.TIMERevent to any registered handlers.
There are also a number of AVM activities that are related to executing your code and supporting the language features of ActionScript:
trace()statements in your ActionScript. You can also view this data in the Trace Log panel.
UncaughtErrorEvent.UNCAUGHT_ERRORto any registered handlers.
Finally, there are some activities that you'll see if you invoke ActionScript 3 code from other languages:
If you use ActionScript workers in your content, they'll appear as separate sessions in Scout. Under the hood, they're really just separate player instances, with an API for sharing data between them. Support for workers in Scout is currently a beta feature. To enable support, you'll need to do the following:
For content that uses ActionScript workers, you might see the following activities in Scout:
In addition to the built-in measurements that Flash Player makes, you can send custom data to Scout using the Telemetry API. The two main calls are as follows:
Telemetry.sendMetric(name, value)– this reports a name-value pair to Scout, which will show up in the Activity Sequence panel. For example, if you call
Telemetry.sendMetric("UserID", 2), you'll see "UserID: 2" appear in the Activity Sequence panel at the time the code gets executed.
Telemetry.sendSpanMetric(name, startTime, value)– this reports an activity to Scout, with an optional value. You record the start time by looking at
Telemetry.spanMarker, and then pass it to
Telemetry.sendSpanMetricat the end of the time period you want to measure. It'll show up in Scout in both the Top Activities and Activity Sequence panels.
For more details, and some examples of how to use this, see the documentation for the Telemetry API.
At the core of Flash Player is the frame ticker–the heartbeat that pulses whenever it's time for a new frame to start. At the start of each frame, it executes any timeline tags, invokes any frame scripts on the timeline, and dispatches a few key ActionScript 3 events. The activities of the frame ticker are as follows:
MovieClip.onEnterFrameevent handler in ActionScript 2.
Event.ENTER_FRAMEand invokes them.
Event.FRAME_LABEL, its handler gets invoked.
Event.EXIT_FRAMEand invokes them.
The display list is the classical approach to rendering in Flash Player. Basically, you get given a blank canvas called the stage, and you draw to it by attaching and positioning graphical entities called display objects. There are different types of display object, including vector art, bitmaps, and text, and they can be nested hierarchically to build up a complicated scene. Whether you interact with display objects from ActionScript, or by positioning them and setting up tweens in Flash Pro, you don't have to worry about how they're actually rendered. Flash Player does the hard work for you, figuring out how to convert the display list into the actual pixels you see on your screen.
If you're using ActionScript to manipulate the display list, you'll notice that there are several APIs you can call to make changes. You can think of these changes as immediate activities. When you call an API, it modifies the internal state of the display list straight away. What it doesn't do is immediately modify what you see on the screen! Instead, Flash Player marks any display objects that you modify as dirty, meaning that they need to be redrawn. Sometime later, after your code has finished executing, Flash Player performs a rendering pass. Effectively, it collects all your changes together, so it only has to update the screen once. You can think of it like stop-motion animation. During a frame, you go around tweaking all the little clay figures, and after you're finished, Flash Player takes the photograph of the scene for that frame.
Here's what a rendering pass looks like in Scout (see Figure 5):
If you're drawing complicated shapes with lots of edges and different filters, then rendering can be expensive. Try to modify the display list as little as possible to reduce the number and size of dirty regions that need to be redrawn on each frame. If you're drawing a complex nested object that you rarely modify (except for moving it around), you can improve rendering performance by caching it. This rasterizes the display object to a cached surface, which only needs to be regenerated if it changes. If you only ever translate the display object, set the
cacheAsBitmap property. If you also want to rotate and scale it, use
cacheAsBitmapMatrix (only supported by AIR on mobile).
Scout shows you the following activities related to cached surfaces (shown in orange in the DisplayList Rendering panel):
As part of each rendering pass, Scout will show you additional information for activities performed on certain types of display object. The following activities relate to rendering text:
textproperty of a TextElement or TextField object), and needs to be laid out again. This will also mark the display object as dirty, so that it gets redrawn in the next rendering pass.
The following activities are related to Bitmap objects and manipulating their associated bitmap data (
There are a few additional operations related to display list rendering that you might encounter in Scout:
updateAfterEvent(). As a rule of thumb, if you've set a high framerate (like 30fps or 60fps) then you should never need to call this, since Flash Player already does at least one rendering pass per frame.
Event.RENDERevent will be dispatched to any registered handlers just before the rendering pass starts.
Stage3D is an ActionScript API that sits on top of OpenGL and DirectX, letting you do cross-platform hardware-accelerated rendering. The way this works is quite different from the traditional display list model, although the Starling framework runs on top of Stage3D and provides a similar API to the display list for 2D content. Stage3D content renders to its own display buffer, which appears on screen behind the display list.
The basic structure of the Stage3D rendering cycle is to first set up the state of the GPU (uploading textures, meshes, and shaders that you want to use), and then issue a number of draw calls that tell the GPU to render batches of triangles to the target buffer. When you've finished constructing the scene, you call
Context3D.present() to actually display it on the screen. There are two places where actual work happens:
When Flash Player starts up a new player instance, it first has to download the main SWF, parse it, and load it into memory before it can start to execute it. You'll see the following activities related to this in Scout (note that there's considerable overlap with the networking component of Flash Player):
Loader.load(). Each time data is received, a
ProgressEvent.PROGRESSevent will be dispatched to any registered handlers.
MovieClipLoader.onLoadInitevent handler in ActionScript 2.
There are three main types of network operation supported by Flash Player: local connections, TCP/HTTP connections, and streaming media. Whichever you use, you typically begin by setting up a network connection or initiating a request. Network operations take time, so they don't happen synchronously. Once you've requested something, your ActionScript code can perform other tasks, while Flash Player handles the network operation in the background. You can find out when the operation has finished, or get status updates, by registering event handlers for the relevant events.
To enable two player instances on the same machine to communicate (for example, if you've loaded a helper SWF), you can set up a LocalConnection object to let one invoke functions of the other. Scout shows you the following activities related to local connections:
functionName(arguments)in the receiving LocalConnection object, as a consequence of a call to
send(connectionName, functionName, arguments)on the sending LocalConnection object.
When you want to download content, like images and other SWFs, you typically use Loader or URLLoader objects, which use HTTP. You can also send and receive data from a server using an HTTP NetConnection. Scout shows you the following activities related to this:
NetConnection.call(), are queued up by Flash Player. Periodically, these queued requests get sent over the network.
NetConnection.call()succeeding or returning an error.
Flash Player also supports a number of protocols for streaming media from a server, like audio and video data. You can create a NetStream using a NetConnection, where you specify the protocol. The Real Time Messaging Protocol (RTMP) runs on top of TCP, and the Real Time Media Flow Protocol (RTMFP) runs over UDP. You'll see the following activities in Scout, related to streaming:
onMetaDataproperty of the
On the majority of desktop computers, Flash Player is efficient at playing sound. Time spent performing sound operations will show up in the ActionScript panel in Scout as calls to functions in the flash.media package, such as on Sound or SoundChannel objects. You might also see the following activity:
Sound.onSoundCompletehandler in ActionScript 2, or the handler for
Event.SOUND_COMPLETEon a SoundChannel object in ActionScript 3.
Video playback in Flash Player is basically a long-running network operation. When you tell Flash Player the video you want to play, it sets off an ongoing activity in the background. Data periodically arrives over the network, is decompressed, and then gets displayed on the screen. The CPU time needed can vary quite a bit, depending on the platform, codecs, and other video settings.
Scout doesn't currently provide much information about your video performance, but you might see the following activity:
There are a few other Flash Player activities that don't really fit into any of the above categories:
When Flash Player is waiting for something to happen, it shows up as inactive time in Scout. This can either be because it's blocking on something, or simply because it ran out of work to do. If you expand the Inactive category in Scout's Summary panel, you'll see this broken down into some or all of the following:
a MessageChannel.send(), or a
As well as measuring how long it spends performing various activities, Flash Player keeps track of how much memory it's using. You can see this in the Summary panel in Scout, and just like CPU time, it's broken down into categories. Flash Player only keeps track of memory that it explicitly allocates–there's some memory that the operating system allocates, such as for storing the Flash Player executable, that won't show up in Scout.
When you look at the memory usage of a session in Scout, it's important to realize that the total memory is the memory used by all the running player instances. Memory used by other player instances appears in the category Other, under Other Players. In addition, some of the memory under Uncategorized may be due to other player instances. Some data structures are shared within Flash Player, making it hard to attribute it to a specific player instance. When you're profiling your content in the browser, it's best to run only your SWF if possible.
Flash Player does its best to keep track of the main data structures that use a significant amount of memory, but some areas are better covered than others. At the moment, it doesn't measure the memory used by audio and video buffers, or by JITted ActionScript code. This will show up under Uncategorized, along with any other internal data structures that aren't tracked.
The GPU is the specialized processor on your graphics card that lets you do hardware-accelerated rendering. It's really good at doing massively parallel computations, like positioning and coloring huge arrays of triangles. That's in contrast to the CPU, which can do much more complicated tasks, but is only really good at doing one thing at a time. Stage3D makes use of the GPU by providing an ActionScript API that lets you talk to it directly. To make life easier, you'll often use a higher-level framework, such as Starling for 2D content, and Away3D for 3D content. Keep in mind, though, that these frameworks are using the GPU behind the scenes.
The thing to remember when using Stage3D is that the GPU runs in parallel with the CPU. This means that the speed of your content is limited by the slowest component. Even if your ActionScript is fast (so that the CPU isn't overloaded), if you give the GPU too much work to do then you won't achieve the framerate you want. Here are two of the most common performance problems when using the GPU:
Context3D.present(), you're telling the GPU to display what you've been drawing, meaning that the GPU should swap these two buffers around. But the GPU only swaps them every 60th of a second. This is known as the VSync, and it's related to how fast a monitor can update its image. If the GPU isn't ready to swap, it'll block, and this shows up as Waiting for GPU in Scout. This normally isn't a problem, but if the CPU can't keep up with the VSync rate, it might repeatedly miss the deadline and then have to wait for the next VSync for the GPU to update. If this happens, you can often improve performance by setting a lower framerate to avoid the extra waiting time.
Here's a great tip: If your content shows excessive Waiting for GPU time in Scout, try temporarily disabling hardware acceleration in Flash Player (right-click and select Settings). This will cause Flash Player to fall back to software rendering, and the waiting time will vanish, so you can confirm that your problem is GPU-related.
Now that you know more about how Flash Player works, you'll be better able to use Adobe Scout to optimize your content. Remember that you can use this article as a reference if you forget what something means while using Scout. Happy profiling!