Think before you create GObjects
I had always been hearing that GObjects are slow and it's not always a good idea to use/write them but I never saw any evidence to support that. I had this desire to write a test application to get this evidence but felt too lazy to do it in C. I realized a few days ago that I can write such an app very easily in Vala without giving up much on my laziness. :) So here is an app that I wrote last evening after returning from vacation. Here are the results on my laptop:
$ ./test-perf
0.000182 seconds taken in creating 10000 structs.
0.001598 seconds taken in creating 10000 instances (compact).
0.003522 seconds taken in creating 10000 instances.
0.090455 seconds taken in creating 10000 instances (GObject).
The ranking is exactly how I expected it to be but didn't expect such a big difference between them all.
$ ./test-perf
0.000182 seconds taken in creating 10000 structs.
0.001598 seconds taken in creating 10000 instances (compact).
0.003522 seconds taken in creating 10000 instances.
0.090455 seconds taken in creating 10000 instances (GObject).
The ranking is exactly how I expected it to be but didn't expect such a big difference between them all.
Comments
Also, check http://bugzilla.gnome.org/show_bug.cgi?id=536939
Use cpu time (times, /usr/bin/time, etc) and run your benchmark much more longer (let's say at least 10s)
Ben.
very nice, but if you create the same object wich gob2 you get
0.000124 seconds taken in creating 10000 structs.
0.001661 seconds taken in creating 10000 instances (compact).
0.002564 seconds taken in creating 10000 instances.
0.093704 seconds taken in creating 10000 instances (GObject).
0.012818 seconds taken in creating 10000 instances (GObject mit gob2).
And I thing if you make it native the result will be better!
Here is the gob2 code
class Gob:Class from G:Object {
public guint f1;
property INT f1 (
nick = "f1",
default_value = 0,
export, link);
public gdouble f2;
property DOUBLE f2 (
nick = "f2",
default_value = 0.0,
export, link);
public GobClass *
new (guint f1, double f2) {
Self * self = (Self *)GET_NEW;
self->f1 = f1;
self->f2 = f2;
return self;
}
}
I've tried running all those tests twice in your benchmark (simply copy&paste the four test_* calls) and got an interesting result:
0.000284 seconds taken in creating 10000 structs.
0.002364 seconds taken in creating 10000 instances (compact).
0.004611 seconds taken in creating 10000 instances.
0.086733 seconds taken in creating 10000 instances (GObject).
0.000204 seconds taken in creating 10000 structs.
0.001020 seconds taken in creating 10000 instances (compact).
0.002177 seconds taken in creating 10000 instances.
0.054395 seconds taken in creating 10000 instances (GObject).
For all 4 tests the second invocation is significantly faster. I'm used to these kind of changes in Java, but without a VM in the background, what would be the reason for this?
Nope but I doubt that will make a significant difference in this context.
very nice, but if you create the same object wich gob2 you get
You code is different since the props are not being set and after reading the comment from rui and the bug he refered to, I am positive that prop get/set is taking most of the time.
That aside, the diff IMHO is still big enough to be careful about inheritence from GObject.
But this is necessary to buil all structure used on a GObject if you do the same test whith a simple c++ class and a QObject will verify the same result.
Thanks.
But this is necessary to buil all structure used on a GObject
Why? It all depends on your requirements from the data-structure in question. If you need to create 10,000 instances within a microsecond, a GObject is certainly not what you want your DS to be. There is a reason why GstMiniObject exists in the gstreamer world. :)
if you do the same test whith a simple c++ class and a QObject will verify the same result.
I am not so sure about that but I hey what do I know about C++. :)
You've essentially just proved that two memory assignment instructions are faster than a memory allocation + memory assignment.. ;-)
I suggest you re-write the struct case by creating an array of pointers and then call g_new0 (or whatever vaia equivalent) on each iteration in the loop to make it more fair.
I am not trying to prove anything at all. I only did a small study and shared my findings and conclusion. I know full-well that comparing gobjects with structs is like comparing apples and oranges, all I am saying is that one should be careful in choosing the DS to use. One do not need to create 10,000 objects within a second in most cases so I am all for GObjects in those cases especially when Vala makes it so easy to write them. :)
I personally think GObject is way too bloated to be the standard base class for all GLib-based libraries.
I would rather prefer to have a lightweight base class, with just the bare minimum (basically, just inheritance and ref counting), and be able to choose the current GObject implementation as an alternative when it really makes sense to do so.
Personally, I don't like the non-gobject type instance generated classes in vala, there is no common base class and in the end we probably will be duplicating some mini like GObject, so you've definitely got my vote in getting something like this in glib.
I think that Vala is performance-ready for classical Object Oriented apps. If you really want high perfs you can recode part of your program in pure C.