Use the GObject hole on 64bit arches for some flags to improve performance

This uses a 32bit hole in the GObject structure on 64bit arches
as a flag field which can be optionally used for some preformance hints.

Currently there is a flag that gets set any time you connect to a signal
on a GObject which is used as early bailout for signal emissions, and using
the flags field instead of a user-data for checking if a GObject is
under construction.
This commit is contained in:
Alexander Larsson 2019-09-02 15:48:32 +02:00
parent cf990faae5
commit 5afd574e91
3 changed files with 128 additions and 6 deletions

View File

@ -162,6 +162,29 @@ enum {
PROP_NONE
};
#define OPTIONAL_FLAG_IN_CONSTRUCTION 1<<0
#define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER 1<<1 /* Set if object ever had a signal handler */
#if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8
#define HAVE_OPTIONAL_FLAGS
#endif
typedef struct
{
GTypeInstance g_type_instance;
/*< private >*/
volatile guint ref_count;
#ifdef HAVE_OPTIONAL_FLAGS
volatile guint optional_flags;
#endif
GData *qdata;
} GObjectReal;
G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal));
G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count));
G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata));
/* --- prototypes --- */
static void g_object_base_class_init (GObjectClass *class);
@ -1008,10 +1031,83 @@ g_object_interface_list_properties (gpointer g_iface,
return pspecs;
}
static inline guint
object_get_optional_flags (GObject *object)
{
#ifdef HAVE_OPTIONAL_FLAGS
GObjectReal *real = (GObjectReal *)object;
return (guint)g_atomic_int_get (&real->optional_flags);
#else
return 0;
#endif
}
static inline void
object_set_optional_flags (GObject *object,
guint flags)
{
#ifdef HAVE_OPTIONAL_FLAGS
GObjectReal *real = (GObjectReal *)object;
g_atomic_int_or (&real->optional_flags, flags);
#endif
}
static inline void
object_unset_optional_flags (GObject *object,
guint flags)
{
#ifdef HAVE_OPTIONAL_FLAGS
GObjectReal *real = (GObjectReal *)object;
g_atomic_int_and (&real->optional_flags, ~flags);
#endif
}
gboolean
_g_object_has_signal_handler (GObject *object)
{
#ifdef HAVE_OPTIONAL_FLAGS
return (object_get_optional_flags (object) & OPTIONAL_FLAG_HAS_SIGNAL_HANDLER) != 0;
#else
return TRUE;
#endif
}
void
_g_object_set_has_signal_handler (GObject *object)
{
#ifdef HAVE_OPTIONAL_FLAGS
object_set_optional_flags (object, OPTIONAL_FLAG_HAS_SIGNAL_HANDLER);
#endif
}
static inline gboolean
object_in_construction (GObject *object)
{
#ifdef HAVE_OPTIONAL_FLAGS
return (object_get_optional_flags (object) & OPTIONAL_FLAG_IN_CONSTRUCTION) != 0;
#else
return g_datalist_id_get_data (&object->qdata, quark_in_construction) != NULL;
#endif
}
static inline void
set_object_in_construction (GObject *object)
{
#ifdef HAVE_OPTIONAL_FLAGS
object_set_optional_flags (object, OPTIONAL_FLAG_IN_CONSTRUCTION);
#else
g_datalist_id_set_data (&object->qdata, quark_in_construction, object);
#endif
}
static inline void
unset_object_in_construction (GObject *object)
{
#ifdef HAVE_OPTIONAL_FLAGS
object_unset_optional_flags (object, OPTIONAL_FLAG_IN_CONSTRUCTION);
#else
g_datalist_id_set_data (&object->qdata, quark_in_construction, NULL);
#endif
}
static void
@ -1030,7 +1126,7 @@ g_object_init (GObject *object,
if (CLASS_HAS_CUSTOM_CONSTRUCTOR (class))
{
/* mark object in-construction for notify_queue_thaw() and to allow construct-only properties */
g_datalist_id_set_data (&object->qdata, quark_in_construction, object);
set_object_in_construction (object);
}
GOBJECT_IF_DEBUG (OBJECTS,
@ -1766,7 +1862,7 @@ g_object_new_with_custom_constructor (GObjectClass *class,
*/
newly_constructed = object_in_construction (object);
if (newly_constructed)
g_datalist_id_set_data (&object->qdata, quark_in_construction, NULL);
unset_object_in_construction (object);
if (CLASS_HAS_PROPS (class))
{

View File

@ -711,7 +711,7 @@ handler_insert (guint signal_id,
HandlerList *hlist;
g_assert (handler->prev == NULL && handler->next == NULL); /* paranoid */
hlist = handler_list_ensure (signal_id, instance);
if (!hlist->handlers)
{
@ -2345,7 +2345,10 @@ g_signal_connect_closure_by_id (gpointer instance,
else
{
Handler *handler = handler_new (signal_id, instance, after);
if (G_TYPE_IS_OBJECT (node->itype))
_g_object_set_has_signal_handler ((GObject *)instance);
handler_seq_no = handler->sequential_number;
handler->detail = detail;
handler->closure = g_closure_ref (closure);
@ -2410,6 +2413,9 @@ g_signal_connect_closure (gpointer instance,
{
Handler *handler = handler_new (signal_id, instance, after);
if (G_TYPE_IS_OBJECT (node->itype))
_g_object_set_has_signal_handler ((GObject *)instance);
handler_seq_no = handler->sequential_number;
handler->detail = detail;
handler->closure = g_closure_ref (closure);
@ -2511,6 +2517,9 @@ g_signal_connect_data (gpointer instance,
{
Handler *handler = handler_new (signal_id, instance, after);
if (G_TYPE_IS_OBJECT (node->itype))
_g_object_set_has_signal_handler ((GObject *)instance);
handler_seq_no = handler->sequential_number;
handler->detail = detail;
handler->closure = g_closure_ref ((swapped ? g_cclosure_new_swap : g_cclosure_new) (c_handler, data, destroy_data));
@ -3121,7 +3130,14 @@ g_signal_emitv (const GValue *instance_and_params,
(node->single_va_closure == SINGLE_VA_CLOSURE_EMPTY_MAGIC ||
_g_closure_is_void (node->single_va_closure, instance)))
{
HandlerList* hlist = handler_list_lookup (node->signal_id, instance);
HandlerList* hlist;
/* single_va_closure is only true for GObjects, so fast path if no handler ever connected to the signal */
if (_g_object_has_signal_handler ((GObject *)instance))
hlist = handler_list_lookup (node->signal_id, instance);
else
hlist = NULL;
if (hlist == NULL || hlist->handlers == NULL)
{
/* nothing to do to emit this signal */
@ -3204,7 +3220,7 @@ g_signal_emit_valist (gpointer instance,
if (node->single_va_closure != NULL)
{
HandlerList* hlist = handler_list_lookup (node->signal_id, instance);
HandlerList* hlist;
Handler *fastpath_handler = NULL;
Handler *l;
GClosure *closure = NULL;
@ -3226,6 +3242,12 @@ g_signal_emit_valist (gpointer instance,
fastpath = FALSE;
}
/* single_va_closure is only true for GObjects, so fast path if no handler ever connected to the signal */
if (_g_object_has_signal_handler ((GObject *)instance))
hlist = handler_list_lookup (node->signal_id, instance);
else
hlist = NULL;
for (l = hlist ? hlist->handlers : NULL; fastpath && l != NULL; l = l->next)
{
if (!l->block_count &&

View File

@ -23,6 +23,7 @@
#include "gboxed.h"
#include "gclosure.h"
#include "gobject.h"
/*< private >
* GOBJECT_IF_DEBUG:
@ -92,6 +93,9 @@ void _g_closure_invoke_va (GClosure *closure,
int n_params,
GType *param_types);
gboolean _g_object_has_signal_handler (GObject *object);
void _g_object_set_has_signal_handler (GObject *object);
/**
* _G_DEFINE_TYPE_EXTENDED_WITH_PRELUDE:
*