Aug 172007

Or: Who in the world is actually linking against GLib without Gtk+?

I’m currently in the process of building some general purpose infrastructure for Rapicorn and because a good portion of the C++ utilities used in Beast and Rapicorn are factored out into an extra library called Birnet. I’m running into similar complexity issues here that we encountered with libglib vs. libgobject vs. libgdk already. I.e. lower level modules cannot reference or use mechanisms provided at a higher level (for instance deriving GHashTable from GObject). This becomes even worse with C++ which lacks support for partial classes (a class definition spanning multiple source files, like implementing gtk_widget_foo() outside of libgtk).

One remedy would be to actually merge libbirnet and librapicorn into a single project and let Beast depend on librapicorn. However, that’s essentially like we started out with GLib and Gtk+ in late 1996 when it got split out of the Gimp package. In June 1998 we split off libglib into a separate package for programs needing C utilities but not libgtk. So now, I’m reconsidering this separation after almost a decade. Reflecting on the move is probably not a bad idea before attempting the opposite with Rapicorn:

How many programs are actually linking against libglib but not libgdk, and is there any real benefit from separation?
If your machine has libglib and a few spare minutes, you can use this command line to find out on your own:

	find `echo $PATH | sed 's/:/ /g'` -type f -perm +111 | xargs ldd 2>/dev/null |
	  awk '/\<libglib-2\>/{ lg+=1 } /\<libgobject\>/{ lo+=1 } /\<libgdk_pixbuf\>/{ lp+=1 }
               END { print "glib=" lg " gobject=" lo " gdk=" lp ;
                     print "glib-only:    " 100*(lg-lo)/lg "%";
                     print "gobject-only: " 100*(lo-lp)/lg "%";
                     print "gdk-lot:      " 100*lp/lg "%"; }'

Here are results from a couple machines I had quick access to:

	Site                       Gdk  GObject     GLib  #Apps                50.0%    12.5%    37.5%     16                 47.1%    17.6%    35.3%     17     19.5%    53.7%    26.8%     41
	my server (sarge):       66.7%    14.1%    19.3%    192
	my laptop (etch):        72.7%    14.8%    12.4%    209
	my desktop (feisty):     72.2%    17.1%    10.7%    252
	64bit KDE desktop (sid): 53.0%    16.7%    30.2%    338

That is, the servers have a quite limited set of GUI applications installed, but across a full fledged desktop the vast majority of applications is linked against libgdk anyway. I found Stefan Westerfelds Amd64 KDE desktop particularly interesting: It actually has the most applications linking against GLib, prolly because it has Mono and installed which both link against GLib these days. Does anyone actually have a system with libglib installed but not libgdk?

Next, let’s take a look at the actual “savings”:

	ls -l \
	-rw-r--r-- 1 root root 596608 2006-11-16 10:26
	-rw-r--r-- 1 root root   9784 2006-11-16 10:26
	-rw-r--r-- 1 root root 237156 2006-11-16 10:26
	-rw-r--r-- 1 root root  14028 2006-11-16 10:26
	                       857576 = 837KB

	ls -l \ \
	-rw-r--r-- 1 root root  102504 2007-03-14 13:44
	-rw-r--r-- 1 root root  395836 2006-10-20 07:44
	-rw-r--r-- 1 root root  235464 2007-01-14 22:27
	-rw-r--r-- 1 root root   88604 2007-03-04 22:21
	-rw-r--r-- 1 root root  528580 2007-03-04 22:21
	-rw-r--r-- 1 root root 3043524 2007-03-04 22:21
	                       4394512 = 4292KB

GtkGdkPangoAtkCairoGlib / GLib ratio: (4394512 + 857576) / 857576 = 6.12.
So the “savings” turn out to be splitting off 5/6th of the GUI stack size. At best, a GLib-only program is saving 4.2MB that way. But then again, those are likely to be in memory already anyway. And most definitely, they reside in a library, available as const .text on the harddisk. To put that into perspective: We’re talking about only a handful megabytes here. In physical size actually less than a hires desktop background image, smaller than some icon themes, or roughly 10% of a full linux-2.6.8 kernel + modules binary footprint (41MB). Also symbol resolution performance doesn’t necessarily improve through splits, Ulrich Drepper has a few words on multiple DSOs during deployment.

Given that selecting Gtk+ subcomponents to allow selective shrinkage for size constrained embedded devices via options is on our TODO list anyway; for this set of libraries: Size is not worth a split!

Of course, other aspects not reflected in library size weigh in much more strongly. Such as the ability to build different user communities around projects (e.g. for Cairo vs. Glib) or the reduced dependency chain (sshfs and Qt wouldn’t depend on GLib if it drew in Pango or Gtk+).
So while for the vast majority of applications the GLib split off doesn’t make a huge difference technically, software evolution arguments make all the difference, definitely justifying a separation. The GLib internal splits are definitely not worth the hassle and conceptual boundaries though, if we ever break binary compatibility again, merging the GLib internal libraries and respective pkg-config packages would be a good idea.

Back to my original problem… I’m now pretty convinced that merging Rapicorn with the Birnet utility library will most likely not pose a problem anytime soon. And Qt 4 kindly demonstrates that splits can also be carried out successfully later on during the game.

Tweet about this on TwitterShare on Google+Share on LinkedInShare on FacebookFlattr the authorBuffer this pageShare on RedditDigg thisShare on VKShare on YummlyPin on PinterestShare on StumbleUponShare on TumblrPrint this pageEmail this to someone

  7 Responses to “17.08.2007 What are ELF libraries good for anyway?”

  1. I belive GTK+ requires quite a few bits of X and other sundry thing wheras Glib only depends on libc. I for one would be most unhappy to see this decoupling go away.

  2. well, that could be mc
    ldd `which mc` => (0xffffe000) => /usr/lib/ (0xb7f07000) => /usr/lib/ (0xb7f01000) => /lib/ (0xb7e41000) => /lib/tls/i686/cmov/ (0xb7e2a000) => /lib/tls/i686/cmov/ (0xb7ce9000) => /lib/tls/i686/cmov/ (0xb7ce5000) => /lib/tls/i686/cmov/ (0xb7cbe000)
    /lib/ (0xb7fad000)

  3. On my home server, I use irssi which depends on glib but is obviously a CLI application. So, no X nor Gtk on that machine.

    IMHO, the C functions of the glib are really useful on their own, I think a better question would be : “who is is using GObject without using Gtk?”

  4. Just an FYI, quite a few console-only applications like to link against glib (irssi is the one that pops up in my head right now) and trust me that I’d be seriously ticked off if I needed GDK+X on a small 256Mb flash-based appliance just to run my IRC client off there.

  5. I think you can fudge partial classes in C++ by simply using #include. It’s not what it’s normally used for, but #include is essentially a simple copy and paste mechanism. I haven’t tried it myself, but I don’t see why you couldn’t put the definition for each class function in it’s own .cpp file (but that doesn’t #include the class’s header) and then have one glue file that #include’s the header and then all of the individual .cpp’s.

  6. Splitting off GLib and GObject for 2.0 was one of the best things that ever happened for GStreamer.

    Before that, I had to run an XVfb server just to be able to run audio-only GStreamer applications on my X-less server. GTK *needs* a running X display just to be able to pass by gtk_init(), it was terrible.

    By now there’s a bunch of programs that use GLib without GTK+. Just in our company, there is GStreamer, Pigment and Flumotion happily running with GLib and without GTK+.

    I also don’t understand why you advocate a merge back of GLib into GTK when your examples are about merging GObject with GLib again so you can fix GHashTable ? Maybe I’m missing something obvious ?

    And how is saving 4 MB for something you don’t need in your app in the first place a bad thing ?

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>