Pushing GObject signals performance by signal/closure micromanagement

Using g_signal_connect() is traditional approach. But not best.

Only one function takes signal_id itself rather than string - g_signal_connect_closure_by_id().
This seems to be what I need, but other questions rise there.

Besides redundant signal string name parsing, there’s another issue: marshallers, which are only useful in language bindings, while in C it’s just pointless waste. Well, at least in case, when marshaller is called per each handler call rather than entire signal emission.

My solution for that would be do resind stock marshallers, placing own callback, which would take all GValue params directly. It doesn’t seem to be hard - although all those g_value_get_{type}() calls bring further level of unreadability into code, maximum 1 call seems required per argument. Also it should be no problem to define own, shorted aliases for them like:

#define int_val g_value_get_int
#define int_set g_value_set_int

But it would be way better if value inside GValue could be used directly.
Thus, question 1: is there way - may be some macroses, doing it in same way as following snippet?

typedef struct {
    GType type;
    union {
        int    v_int;
        double v_double;
        ...
    };
} GValue;

#define G_VALUE_INT(value) (value.v_int)

...

GValue var;
...

// get value
int v = G_VALUE_INT(var);

// get value address
int *p = & G_VALUE_INT(var);

// set value (not so necessary, but would be great too)
// Also need check if braces are legal around lvalue in C
G_VALUE_INT(var) = 80;

Next question (2) - about mentioned closure micromanagement. Callback at marshaller post obviously wins by performance over variant with separate useless marshaller (which does nothing but gvalue->args & return->gvalue copying), but I’m still in doubt:
Does it really make sence against gtk/gdk, gstreamer (whatever I’m going to meet) or their C code is already optimized in this way?

And side question (3).
For begining I decided to look inside existing closure, from existing connection, to see, how it’s arranged, and possibly find answer for question 2. But to my suprise, I found no way to get closure from signal+handler_func pair.
I only see function to find handlers, but it uses closure to search by, rather than to search for.
Is there any way to get closure from connection, at least if I know, what function is connected to it by g_signal_connect?

Well, perhaps I could eventually understand it, looking into gobject source code, but not expecting it to be quick.

Meanwhile I could make a proposal for gobject:

  • Signals could be available not only by string/id, but by handle pointer as well. Same about handlers. Functions names to find handlers could be like:
g_signal_find_by_name(char *)
g_signal_find_by_id(unsigned)
g_signal_handler_find_by_name(char *)
g_signal_handler_find_by_id(unsigned)
g_signal_handler_get_closure(sighandler)
  • Signal/handler functions should use handle pointer for object argument instead of string. Those, taking strings, would be considered as convenience wrappers.

Are you actually encountering demonstrable performance problems with GObject signals — are they showing up in a performance profile of your application?

Of course, you cannot say something is only useful in language bindings but not in C, because the signal API is consumable via language bindings. If you don’t care about language bindings you don’t really need signals: just use a C function pointer.

Have you also looked at the va_list marshallers, which cut boxing and unboxing into GValues?

Marshallers generated by glib-genmarshal will use direct GValue field access if you build without G_ENABLE_DEBUG.

It seems you have not looked at the GObject code at all, and you’re mostly running on a bunch of assumptions.

We literally cannot change most of the signal machinery without breaking backward compatibility; we tried, and failed, over the years. While it’s entirely possible a bunch of the people involved with GLib for the past 20 years may have missed something, I somehow doubt that to be the case.

That was on surface. I was attracted by very fact, that existing apps always reference signals by name, which is essential when employing standard (not for tinkerers) way to operate with signals - connect, emit, block, etc. As for closure & marshallers - well, they just got on my way, because found no other way to connect signal by its id than g_signal_connect_closure_by_id().

Of course, you cannot say something is only useful in language bindings but not in C, because the signal API is consumable via language bindings.

I know their use in C - just translate between GValue and plain C variables. Entire call layer just for this.
May be ability to connect functions with different arguments (typically those without such) is also due to them, but that’s certainly assumption.

If you don’t care about language bindings you don’t really need signals: just use a C function pointer.

Apps still need to use them.

Marshallers generated by glib-genmarshal will use direct GValue field access if you build without G_ENABLE_DEBUG.

Ha… looks like recipe: generate some pointless marshallers (taking single argument of required type), extract code, reading gvalue (well… I know, easier to say than do)))).

It seems you have not looked at the GObject code at all, and you’re mostly running on a bunch of assumptions.

Yup. May be I forgot to say this. Still have to take a look.

We literally cannot change most of the signal machinery without breaking backward compatibility; we tried, and failed, over the years.

I did not propose yet to change API. But imho, few more convenience wrappers or just functions, doing something more optimally, would break nothing. E.g. functions, accepting only string to identify string, property, etc - could become convenience wrappers for those, taking their index or handle.

Of course, my way for direct value access in GValue should be marked as unsafe or at least use assert for this check. All who use it should know what they do.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.