Sunday, 17 February 2008

be quick or be dead

Note, image has not much to do with this post, it's emacs22 running on my N810. More about that in my next post

For a while, we've been doing semi-weekly updates to modest, usually on Friday's. And every week, we're fixing many issues, big and small. Modest has grown quite a bit, and modest + tinymail/camel has about 240K lines of code. That's quite a haystack for bugs to hide, but I'm quite happy with the speed at which we've been able to squash them. The big fix of last week was adding support for maemo-launcher, due to heroic hacking efforts by dape. Maemo-launcher significantly improves startup speed. How does it do that? To answer that, let's look at what happens at application startup time.

still haven't found what i'm looking for

Few people have the time to write a UI-toolkit, or even printf(3), for every piece of software they develop. Thankfully, we can reuse libraries to do such things for us. The most common form ar dynamic libraries. With the ldd(1) utility, you case see which ones your application uses. For example, for modest:

[sbox-CHINOOK_X86: ~] > ldd /usr/local/bin/modest => (0xffffe000) => /usr/lib/ (0xf7ef9000) => /usr/local/lib/ (0xf7ef6000) => /usr/local/lib/
.... (69 others) ....

Now, when we start modest, we must load these libraries. Suppose, somewhere in the code, we have:

magic_check = gtk_check_button_new_with_label ("Enable magic");

When starting the program, the dynamic linker will now have to figure out at what memory location gtk_check_button_new_with_label, is to be found. And not just that function... if we look at modest, we can find the number of external function (or more general, symbols), with the nm(1) utility:

[sbox-CHINOOK_X86: ~] > nm -u /usr/local/bin/modest
U gtk_check_button_new_with_label
U gtk_check_menu_item_get_active
U gtk_check_menu_item_get_type
U gtk_check_menu_item_set_active
U gtk_clipboard_get

In total, there are are almost 1300 external symbols in just modest; and this is only a fraction of the total, as GTK+ will have a lot of external symbols as well (think Glib, Pango, ...). In total, there will be many thousands. Without going into the details, it takes a significant amount of time to do the symbol lookup. Even on a fast desktop machine it can cause noticeable delays (esp. for C++), and more so on 770/N8x0.

Another factor that affects application startup significantly is initialization: in GTK+-based applications, e.g. gtk_init takes quite some time. In particular, applying the GTK-theme is slow on 770/N8x0, because the default theme file (gtkrc) is huge: the default theme on Ubuntu ("Human") contains 242 lines, but on my N810 default theme has 7046 lines, on much slower hardware. And note that the theme is very picture-heavy, and many little images must be loaded. To get an idea of how much work must be done you can use strace(1) when starting an application... scary stuff.

And finally, another slowdown is the physical loading of all these libraries into memory. This will typically only happen the first time, as Linux will keep the data around as long as there is enough memory (note, for testing, you can force a flush with echo 3 > /proc/sys/vm/drop_caches).

i remember now, i remember how it started

What can we do about all this slowness? Enter maemo-launcher.

Maemo-launcher is a daemon that loads common libraries at startup, does a gtk_init for you, and as such, the price for doing all this work (as explained above) is only paid once. Your actual application is compiled as a dynamic library. When your application is started, maemo-launcher forks, this dynamic library is loaded, and we jump to straight to its main-function. This totally gets rid of the initializations, theme loading and such mentioned above, and saves quite some startup time.

How much are the improvements you can expect? Johannes Schmid did some testing, and found improvements of about 25% for small programs. Some non-scientific testing for modest shows that the startup-time is 1-2 seconds faster. Does that matter? Well, let's look at our 10 million ;-) modest users, all of them starting modest once a day. With a 1 second improvement, every day we save almost 4 months of time! Jokes aside, application startup times are very visible to users, and really determine whether they consider your software fast and snappy, or huge and slow; it's time well spent trying to improve that.

parting thoughts

For a very practical way of how to get maemo-launcher working with your program, see the Appendix of the MaemoMM-tutorial. It also shows how to add additional libraries to the set loaded and initialized by maemo-launcher. Recommended reading.

Note; nm as shown above does not work with stripped binaries (i.e. the binaries of which the symbol got removed). And unless you compile everything yourself, most of the binaries on your system will be stripped. However, you can still get much of the information with objdump -R; however, it it's not equivalent to nm -u, and will contain some non-external symbols.

Finally, this is the kind of post that could contain embarrassing factual errors :-) please check the comments for updates.

No comments: