[gtk/gtk-3-24: 1/2] gtkbuilder: Eliminate array reallocations in get_parameters()



commit fb998ff52d15d00eaba69e9d573195f08c5564fe
Author: Philip Withnall <withnall endlessm com>
Date:   Wed Jun 10 19:59:03 2020 +0100

    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>

 gtk/gtkbuilder.c        | 20 ++++++++++++++++++--
 gtk/gtkbuilderparser.c  |  1 +
 gtk/gtkbuilderprivate.h |  1 +
 3 files changed, 20 insertions(+), 2 deletions(-)
---
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index d525d952c3..93be663fa5 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -458,6 +458,7 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
                             GType        object_type,
                             const gchar *object_name,
                             GSList      *properties,
+                            gsize        n_properties,
                             GParamFlags  filter_flags,
                             GArray      **parameters,
                             GArray      **filtered_parameters)
@@ -466,10 +467,23 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
   DelayedProperty *property;
   GError *error = NULL;
 
+  /* Create the two arrays with size @n_properties. The total number of elements
+   * between them will eventually be @n_properties, but it’s 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 it’s full).
+   *
+   * @n_properties is typically ≤ 8, so it’s
+   *  (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 it’s a fairly hot path. */
   if (parameters)
-    *parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
+    *parameters = g_array_sized_new (FALSE, FALSE, sizeof (GParameter), n_properties);
   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)
     {
@@ -670,6 +684,7 @@ _gtk_builder_construct (GtkBuilder  *builder,
   gtk_builder_get_parameters (builder, info->type,
                               info->id,
                               info->properties,
+                              info->n_properties,
                               param_filter_flags,
                               &parameters,
                               &construct_parameters);
@@ -814,6 +829,7 @@ _gtk_builder_apply_properties (GtkBuilder  *builder,
   gtk_builder_get_parameters (builder, info->type,
                               info->id,
                               info->properties,
+                              info->n_properties,
                               G_PARAM_CONSTRUCT_ONLY,
                               &parameters, NULL);
 
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 121935502e..5ee5423b17 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -1112,6 +1112,7 @@ end_element (GMarkupParseContext  *context,
             }
 
           object_info->properties = g_slist_prepend (object_info->properties, prop_info);
+          object_info->n_properties++;
         }
       else
         g_assert_not_reached ();
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 06b9a7aefc..28d100a872 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -36,6 +36,7 @@ typedef struct {
   gchar *id;
   gchar *constructor;
   GSList *properties;
+  gsize n_properties;
   GSList *signals;
   GSList *bindings;
   GObject *object;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]