gtkmm code size

I posted this to gtkmm list today. I’ll repeat it here because smart people read planet GNOME.

I have recently done some work to optimize gtkmm for code size, for use on embedded devices, such as Nokia’s 770 internet table. I was concerned that, for instance:

  • the _stripped_ code size of gtkmm 2.6’s libgtkmm .so is 2.5M on i686,
  • which is comparable to the size of libgtk-x11’s .so (version 2.8 is 2.9M).

It didn’t seem right that the wrapper is as big as the thing it wraps, because our method implementations are so small.

I have discovered that a large part of this code size is explained simply by the size of our API, and we have a lot of extra convenience API compared to GTK+. Simply exporting a symbol adds to the code size. I shaved a few 100K off the .so size just by using the static keyword on private functions, to stop them being exported. The 2.5M number above is including these easy optimizations.

Optional API

I also added some glibmm configure options to remove rarely-used API. Remember, nothing will change on regular linux distros, because they won’t use these options:

  • –enable-api-properties=no
    You can use get/set_property<>(“propertyname”) instead.
  • –enable-api-vfuncs=no
    These are rarely needed. You can use the C function pointer instead.
  • –enable-api-exceptions=no
    Methods that throw an exception have an extra std::auto_ptr& output parameter when this option is used. This allows use of the “-fno-exceptions” CXXFLAGS, which does not have much affect, but being able to use “-fno-exceptions” means that “-g -Os” (Optimize for code size) can start to be significant. Together they reduce code size by about 15%.
  • –enable-deprecated-api=no
    Do not build deprecated API, such as Gtk::FileSelector. This option has been in tarballs for a few months already. This must be specified to gtkmm as well as glibmm.

These options will soon be in releases of glibmm 2.6, 2.8, and 2.10. Necessary changes will be in gtkmm 2.6, 2.8, and 2.9/2.10. Until Cairo is fully optimized, people are using GTK+/gtkmm 2.6 on embedded platforms, but some are using glib/glibmm 2.10.

Together, with the mentioned CXXFLAGS, these reduce total code size for all of the gtkmm 2.6 .so files by around 800K, or 23%.

Virtual methods

However, there is another huge gain to be made by removing default signal handlers, such as Gtk::Button::on_clicked(). The problem is that they are virtual methods, so we have to pay for them even if we don’t use them. We must pay for them in – code size: That’s a lot of symbols in libgtkmm, and the symbols must be listed in applications that use libgtkmm. – load time: the symbols must be resolved by applications that use libgtkmm.

Gtk::Widget has about 60 signals, so that’s at least 60 virtual methods for each widget. As far as I can tell, this increases the code size of each derived class, even if a derived-of-derived class, maybe due to multiple inheritance.

I have not yet committed this configure option to cvs, because this is the most disruptive option. Overriding virtual methods, instead of connecting signals, is quite convenient, but I don’t think it’s worth the cost on an embedded platform just for convenience. I might commit it later. The patch is here: http://bugzilla.gnome.org/show_bug.cgi?id=341380

It removes an extra approx. 500K from the libgtkmm code size.

I also have a simple patch to remove libatkmm, which is not yet very useful on embedded devices.

Failed attempts

  • I have not noticed any significant reduction in code size by using the -fno-inline CXXFLAGS. I had thought that there might be a lot of RefPtr or sigc:: inlining, but I guess that the implementations of these functions are not significantly larger than a function call itself.
  • I have tried ripping libsigc++ apart to reduce the number of templates and template parameters that are used, with almost no effect. Based on this, and my other code-size investigations, I no longer believe the commonly-mentioned theory that libgtkmm is large because of too many template instantiations. I’d gladly be proven wrong.
  • I can strip a few more 100K from the code size by turning the /private/*.h callbacks into functions instead of class methods, and marking them as static, but I would then need to make various protected parts of the classes public.
  • I tried hacking libtool to allow me to use regex to exclude anything with “Anonymous” in the name, to exclude anonymous namespaces, but it made no difference to code size, though the symbols no longer showed up in the nm output. Obviously there’s more to it than that.
  • I tried various values with -falign-functions, but could not achieve anything other than making the code size larger.
  • -fvisibility-inlines-hidden increases the size of libgtkmm by about 20%.

Summary

I believe that the remaining code size is just a fact of life of using C ++, and of having such a large (capable) API. In particular, we must list all those symbol names, and C++ symbol names are larger than C symbol names, due to the (wanted) namespaces and mangling. I can imagine that the linker/ELF could some kind of compression, but that would slow down load times.

But it is rather difficult to analyze code size: https://www.murrayc.com/blog/permalink/2006/02/15/c-code-size/ so I hope that some of you have some more ideas.

These options (and the patch) do at least mean that developers would not have to pay for things they don’t use. That is the C++ philosophy.

5 thoughts on “gtkmm code size

  1. “I can strip a few more 100K from the code size by turning the /private/*.h callbacks into functions instead of class methods, and marking them as static, but I would then need to make various protected parts of the classes public.”

    I don’t know if this is applicable, but have you ever heard of the:

    #define protected public
    ..
    #undef protected

    hack?

  2. ingo, you gave me hope for a moment. But, actually, this increases the stripped size of libgtkmm (HEAD) from 2.8M to 3.3M.

  3. Thats strange, I’ve never seen that option /increase/ code size! As far as I know, that could only happen if there is significant portion of inlineable code shared /between/ DSOs. I often use it on python wrapper (with boost::python), where it sometimes reduces DSO size by half…

    I got most of my info from Ulrichs paper on DSOs (http://people.redhat.com/drepper/dsohowto.pdf). If you didn’t already, check it out. At least the explicit visibility specs should be applicable to libgtkmm.

Comments are closed.