It is important that phone users do not have to wait long for applications to start. This document summarizes the techniques for optimizing application start-up time.
The techniques discussed in this document are limited in scope to application programming. In other words, they do not require modification of any core OS components or servers. Some of the techniques are general good practice and are useful to developers at all levels.
The techniques can be summarized as follows:
Avoid causing unnecessary code to be executed as an effect of your code.
Defer construction of objects, loading of dynamic libraries (for example, the application model), and starting servers until you need to use them.
Write efficient code!
Note: There is an inevitable trade-off between shortening the start-up time and introducing latency elsewhere in the application. For example, if you don’t connect to a server at start-up, you will have to do it later on, when the application needs to use the server. This may be overcome by a central component that coordinates background connections, or construction, such as the view server.
Do not assume you know which methods take the most time. Pay close attention to iterative and recursive operations. Many useful tools and techniques are available to help identify poorly performing code:
Software analysis tools, for instance GlowCode and MetroWerks CodeTEST
The Symbian profiling tool, profiler.exe, which is supplied on DevKits
The RDebug class provides some profiling functions
The TTime class can be used to time blocks of code, but beware of context switches, otherwise you may end up timing other threads!
Deliberately slowing down suspect blocks of code can reveal whether or not optimizing it would bring a significant performance improvement.
Many applications instantiate their components, for instance error handlers, dialogs and menus, during start-up. In turn, each of these components may instantiate other components, for instance menu resources and icons. This can bring some benefits, for instance reducing application complexity, revealing memory allocation problems at start-up rather than after the application has been running for some time and improving the runtime performance of the application. However, to minimize application start-up time, it is recommended to avoid this behavior. Your goal should be to only do what is immediately necessary during start-up.
During application
start-up, only construct UI components that appear in the application’s
initial view. This applies especially to the application's implementations
of CXxxApplication::CreateDocumentL()
, CXxxDocument::ConstructL()
and CXxxDocument::CreateAppUiL()
, all of which
are called before CXxxAppUi::ConstructL()
. Do not
read bitmaps, resources, or any other data associated with the UI
from files unless it is necessary.
In CXxxAppUi::ConstructL()
, make sure CCoeControl::ActivateL()
and CCoeControl::DrawNow()
are called on all controls that
must be drawn when the application is launched. Also ensure that the
client-side window server command buffer is flushed by calling Flush()
on the application's window server session. This
ensures that there aren't any drawing commands left in the client-side
buffer, after CCoeControl::DrawNow()
has completed.
Often, when a large number of small images are required by an application, the overhead associated with loading each bitmap outweighs any benefit associated with their size. Some possible ways to avoid this are:
use text instead,
for very simple graphics, draw directly using drawing primitives rather than loading a bitmap,
concatenate many small bitmaps into one large bitmap file to reduce the need to search for and load multiple files.
Some GUI components redraw themselves
every time their data changes. This may not always be necessary. Complicated
GUI components should implement theirDraw()
method
to only update the area of the screen that has changed. For example,
there is no point in redrawing a whole list box every time a new item
is appended to it. In such cases, a GUI API should allow you to switch
off redrawing. Beware of GUI methods that cause the object they are
called upon to redraw itself.
Use CCoeControl::DrawDeferred()
in preference to CCoeControl::DrawNow()
if possible,
because excessive use of CCoeControl::DrawNow()
can cause GUI flicker. For an explanation, see the documentation
for CCoeControl::DrawDeferred()
.
Uncompressed ROM-based bitmaps that can be used in place from ROM are approximately three times faster to use than filestore bitmaps. Using them can bring a significant reduction in application start-up time.
Specifying bitmap= instead of file= in the .OBY and .IBY files when building the ROM causes bitmaps to be uncompressed before inclusion in the ROM. Other bitmaps need to be uncompressed at runtime, which impacts performance.
The drawback of such bitmaps is that they are large (up to 3 times larger than file based) and cannot be compressed, although decompressing bitmaps should probably be avoided during start-up anyway due to the extra processing required.
If ROM space is limited, consider using such ROM-based bitmaps only if they are displayed during application start-up.
When bitmaps are drawn to the screen, optimum performance is achieved by ensuring that:
Bitmap color
depth = Window color depth = Screen device color depth
If this is the case, no palette mapping between the different color depths is needed. On real hardware, this optimization has been found to improve drawing speed by up to ten times. However, in order to match the screen and window color depth, bitmaps may need to increase in size and so this optimization is only possible if the increase in ROM or RAM usage is acceptable.
The file server can be a major bottleneck during start-up when virtually all threads are searching for and loading data, libraries and plug-ins. Therefore reducing file access is one of the most effective ways to improve performance.
Resource files are used for localization and allow modifications to be made to an application without the need to rebuild it, but they are expensive to use because they require access to the file system.
Many applications on a smartphone do not need to use documents, for example Telephony, Contacts (this uses the contacts database), Browser and Messaging.
By not specifying a default document filename, hundreds of milliseconds can potentially be saved from such applications' start-up time.
If an application uses a document file, application start-up may involve the following steps:
reading the name of the last used document file from the application’s .ini file,
opening the document file, or if one doesn't exist, creating a default document file, after reading its name from the application's resource file,
writing the name of the last used file to the application’s .ini file (which is created if it doesn’t exist),
writing an entry to the most recently used file list (mru.dat),
additional document-related processing within CEikonEnv::ConstructAppFromCommandLineL()
.
The default document's name is read from the application's
resource file by CEikAppUi::ProcessCommandParametersL()
. There are two ways of preventing the application from using a default
document file:
Give the default document a NULL name in the resource file:
RESOURCE TBUF { buf=""; }
Override CEikAppUi::ProcessCommandParametersL()
to zero the document name and return EFalse. This method is slightly
more efficient because it avoids reading the default document name
from the resource file altogether:
TBool CMyAppUi::ProcessCommandParametersL(TApaCommand /*aCommand*/, TFileName& aDocumentName, const TDesC8& /*aTail*/){aDocumentName.Zero();return EFalse;}
This can be a cause of unnecessary file server use.
To prevent excessive drive access and scanning, always specify a drive letter in file paths, if known. The omission of a drive letter will cause all available drives to be searched in the standard Symbian platform order, in which Z: is always searched last.
Server requests involve context switching and may cause the server to run instead of the application. In the worse case if you make a request to a server that has not yet been started you may cause the server to start. This will involve creating a new thread (and possibly process) and running any server initialization code.
Synchronous operations or methods (particularly for server requests) can cause general application slowness, and in particular, a significant reduction in responsiveness. Synchronous requests to servers mean your thread is waiting, so that no start-up progress is being made.
No 'Golden Rule' exists about when to avoid synchronous requests. However, if an asynchronous version of a method exists, it is a good indication that the synchronous method could potentially take some time. Whilst it may take a little extra effort to handle asynchronous versions of method calls, you should consider very carefully any decision to use the synchronous version. It’s often easier to change from using an asynchronous version to synchronous than vice versa.
Note that in some situations, you might know that the server is implementing your asynchronous request synchronously. If this is the case, and the server runs with a higher priority than your application, then both versions of the API may have the same performance. However, using the synchronous version in this case has the drawback that it relies upon knowledge of the server's implementation, which could potentially change.
Opening a connection to a server is an expensive operation. If an application uses a server frequently then it should create one connection and leave it open until the application is destroyed. R classes declared as temporaries (on the stack, in other words) within a method may be a sign of this behavior.