C/C++ Guides


Optimizing Mobile Applications

When you develop a mobile app you want it to be as fast and as memory-efficient as possible. Mobile devices have slower CPUs, limited memory, and limited power resources. Always bear in mind the limitations of the devices you are writing apps for. Here we provide some guidelines to help you optimize your application's performance, size, and power consumption.

MoSync Libraries and Tools

MoSync includes many libraries that we have carefully optimized for performance on a wide range of devices. Try to use these libraries wherever possible. In particular, if you are developing C++ applications, make full use of:

  • The Moblet framework for event handling -- it keeps battery usage to a minimum.
  • MoSync's MTXml parser -- it is optimized for rapid parsing of XML yet keeps memory usage to a minimum.
  • The MAUI graphical user interface library -- it only redraws GUI elements that change and uses caching mechanisms to improve speed.
  • The MAUtil storage classes -- String, Vector, Set, Map and HashMap are efficient when correctly used.

Code Optimization via Pipe-Tool

Pipe-Tool is MoSync's code transformation engine. It is, among other things, a code verifier and optimizer and it performs dead code elimination to produce small, efficient output files.

Pipe-Tool is automatically invoked by the MoSync IDE during the normal build process. It can also be run on-demand from the command line. The dead code elimination process is optional and still in an experimental state, but you can turn it on by enabling Activate Dead Code Elimination in your project's build settings (select Project > Properties > MoSync Project > Build Settings > Compiler Flags) or, on the command line, by passing the -elim flag to Pipe-Tool. 

Asynchronous Operations

We've chosen to not support threads directly in MoSync, but instead we have implemented all essential threaded operations asynchronously. Whenever you start such an operation, it will run in the background and send a notification in the form of an event when it's done. By encapsulating asynchronous operations in the runtime we can ensure that the behavior is consistent on all devices, and you never have to care about thread synchronization. Some platforms implement asynchronous operations internally without threads, for instance Symbian, where they strongly encourage the use of Active Objects over threads.

To keep you application responsive when it's doing a lot of computations, it is a good idea to periodically check for events. This can be done either by invoking some event handling mechanism or by using the Moblet framework, and by doing the computations in blocks so that the execution returns to the main loop from time to time. It is also necessary to make sure the EVENT_TYPE_CLOSE event is received and handled, as it indicates that the application has been forced to quit for some reason.

Dealing with Hardware Limitations

Slow CPUs

The CPUs in many mobile devices are based on the ARM architecture. The CPUs can have different clock frequencies and cache sizes, and some have no cache at all. What mobile CPUs generally have in common is that they are all far less powerful than the ones you're used to develop for on a regular PC.

Any piece of code that is going to use a lot of CPU time should be carefully optimized. Finding the right algorithm is more likely to be important than doing low-level optimizations. Also such optimizations are usually done by the compiler (in our case GCC), so it is always good to know your compiler in order to keep the amount of work to a minimum.

If you're in need of extreme performance, basic speed optimization tricks can help:

  • Pre-calculate stuff
  • Unroll loops when possible
  • Inline small and frequently used functions
  • Give as much information to the compiler as you can (const etc.).

Many mobile devices do not have a floating-point unit (FPU). Their floating-point operations are done in software. That results in much slower processing than on regular PCs. To improve performance, the best way to deal with decimals on these devices is to use fixed-point arithmetic with integers (see, for example, http://en.wikipedia.org/wiki/Fixed-point_arithmetic).

MoSync syscalls (defined in maapi.h) incorporate platform-dependant code that is optimized for the architecture they are running on. Try to use them as much as possible, even if the same functionality is possible to re-implement in other ways. This will not only make things faster, but also smaller.

Divisions may be really slow as they aren't available natively on most ARM processors. Use shifts when possible or pre-calculate reciprocal 1/x values and do multiplications instead.

If you have to read a lot of small pieces of data from a data object, try to buffer data in memory. Doing a lot of calls to maReadData may be slow.

If your application needs to read from and write to memory a lot, try to access memory as integers whenever possible. This is even more important when targeting Android and Java ME devices. XML parsing, for instance, relies on heavy use of byte accesses. Whenever possible use some more approriate binary format.

Try experimenting with different GCC optimization flags (right-click on your project and select Build Configurations > Manage > Build Settings > Compiler Flags > Additional GCC Switches). The -o3 swtich should in most cases be slightly faster than -o2, although it may slightly enlarge the size of the binary.

It's worth mentioning that if you are used to programming in Java, you tend to use as few classes as possible, due to their size and speed overhead. But C++ objects are pretty lightweight and faster than their Java equivalent so you do not need to be so restricted.

ARM Recompiler

MoSync features a recompiler that transforms MoSync code into native ARM code at start up. For CPU-intensive applications, the recompiler can provide a substantial speed boost. As of June 2010, this recompiler is available on Windows Mobile, Symbian s60v3 and s60v5. Work is in progress to bring the recompiler to s60v2 and Android. (on our MDLbenchmark test on Symbian 5th edition, the MoSync ARM recompiler increased performance by more than 300%.)

Battery Power

Battery power is drained when hardware is in use, especially the CPU. It's therefore important to optimize for performance, making the CPU complete its work faster. The syscall maWait puts the thread in suspend mode until an event has been sent, leaving the CPU for use by other threads/processes, thus using less CPU and battery power. The Moblet framework does this automatically.

Screen Size

All phones have different screen sizes. They are often very small with low resolutions, making it more important to choose graphics wisely. Fonts or images should be visible on all sizes of screens.

Data Storage

Many mobile devices have small RAM sizes and most likely a lot better permanent storage capabilities. It's important to take this into account when developing applications for them. Unloaded binaries (.ubin) and unloaded media files (.umedia) are good formats to use for resources as they will be kept in permanent storage as long as they aren't used. (For more information about these resource types, see the Resource Compiler Reference.)

Resources such as images and media files can take up a lot of space in the final package so try to keep them as small as possible. There are many free and commercial tools available for optimizing and compressing resources without noticeably reducing their quality. Generally, you should use the PNG format for images: we have ensured support for it across all capable devices (PNG is generally smaller than GIF anyway). If you have photographic images, you should to use the JPEG format. Most modern devices support it, although some of the older ones may not so it may be worth checking.

Try not to do too many small heap allocations as this will fragment the heap. The result will be slower heap allocations and more memory usage. As you code in C or C++ it's also good to verify that you haven't got any memory leaks. This can be done using a simple trick shown in MAP/MemoryMgr.h: basically you save information about in which file, function, or line every allocation is done, and remove that whenever it is freed. This information can be dumped at anytime to see what memory leaks you have.

If you have more tips about optimizing applications for mobile devices, please share them with us by leaving a comment below.

MoSync SDK 3.3
Copyright © 2013 MoSync AB