gtkbuilder: Eliminate array reallocations in get_parameters()

`gtk_builder_get_parameters()` is a hot path, being called twice for
each object in each UI file in an application. The majority of objects
have ≤ 8 properties, which are each filtered into either `parameters` or
`filtered_parameters`.

Unfortunately, both of those arrays are created as empty `GArray`s, and
adding 8 elements to an empty `GArray` hits the worst possible case of
reallocating and `memcpy()`ing the array 3 times. As the array size is
doubled with each reallocation, the cost is not particularly well
amortised when the array size is small.

From the `ObjectInfo`, we actually know how many properties there are in
total, so just allocate the arrays at the right size to begin with.

This saves 7% of the instruction cycles needed to start up
gnome-software to the point where it’s showing its main window,
according to callgrind. gnome-software is making around 5500 calls to
`gtk_builder_get_parameters()`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
Philip Withnall 2020-06-10 19:59:03 +01:00
parent 996e22bde9
commit fb998ff52d
3 changed files with 20 additions and 2 deletions

View File

@ -458,6 +458,7 @@ gtk_builder_get_parameters (GtkBuilder *builder,
GType object_type, GType object_type,
const gchar *object_name, const gchar *object_name,
GSList *properties, GSList *properties,
gsize n_properties,
GParamFlags filter_flags, GParamFlags filter_flags,
GArray **parameters, GArray **parameters,
GArray **filtered_parameters) GArray **filtered_parameters)
@ -466,10 +467,23 @@ gtk_builder_get_parameters (GtkBuilder *builder,
DelayedProperty *property; DelayedProperty *property;
GError *error = NULL; GError *error = NULL;
/* Create the two arrays with size @n_properties. The total number of elements
* between them will eventually be @n_properties, but its more important to
* avoid realloc()/memcpy() calls on these arrays than to be tight with memory
* allocations (and overallocating by 100% is no worse than what #GArray does
* internally with doubling its size every time its full).
*
* @n_properties is typically 8, so its
* (a) not much of an impact to overallocate
* (b) disproportionally subject to realloc()/memcpy() since the array size
* doubles 3 times in the first 8 elements
*
* gtk_builder_get_parameters() gets called twice for every object in every
* #GtkBuilder file, so its a fairly hot path. */
if (parameters) if (parameters)
*parameters = g_array_new (FALSE, FALSE, sizeof (GParameter)); *parameters = g_array_sized_new (FALSE, FALSE, sizeof (GParameter), n_properties);
if (filtered_parameters) if (filtered_parameters)
*filtered_parameters = g_array_new (FALSE, FALSE, sizeof (GParameter)); *filtered_parameters = g_array_sized_new (FALSE, FALSE, sizeof (GParameter), n_properties);
for (l = properties; l; l = l->next) for (l = properties; l; l = l->next)
{ {
@ -670,6 +684,7 @@ _gtk_builder_construct (GtkBuilder *builder,
gtk_builder_get_parameters (builder, info->type, gtk_builder_get_parameters (builder, info->type,
info->id, info->id,
info->properties, info->properties,
info->n_properties,
param_filter_flags, param_filter_flags,
&parameters, &parameters,
&construct_parameters); &construct_parameters);
@ -814,6 +829,7 @@ _gtk_builder_apply_properties (GtkBuilder *builder,
gtk_builder_get_parameters (builder, info->type, gtk_builder_get_parameters (builder, info->type,
info->id, info->id,
info->properties, info->properties,
info->n_properties,
G_PARAM_CONSTRUCT_ONLY, G_PARAM_CONSTRUCT_ONLY,
&parameters, NULL); &parameters, NULL);

View File

@ -1112,6 +1112,7 @@ end_element (GMarkupParseContext *context,
} }
object_info->properties = g_slist_prepend (object_info->properties, prop_info); object_info->properties = g_slist_prepend (object_info->properties, prop_info);
object_info->n_properties++;
} }
else else
g_assert_not_reached (); g_assert_not_reached ();

View File

@ -36,6 +36,7 @@ typedef struct {
gchar *id; gchar *id;
gchar *constructor; gchar *constructor;
GSList *properties; GSList *properties;
gsize n_properties;
GSList *signals; GSList *signals;
GSList *bindings; GSList *bindings;
GObject *object; GObject *object;