[glib/gobject-speedups4: 10/11] gobject: Speed up property lookup




commit 79d7d3eba575804df83c857d2fb1e19740315dda
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri May 20 20:49:05 2022 -0400

    gobject: Speed up property lookup
    
    When the param specs are provided as an array
    with g_object_class_install_properties, keep
    a copy of that array around and use it for
    looking up properties without the param spec
    pool.
    
    Note that this is an opportunistic optimization -
    currently, it only works for properties of the
    class itself, not for parent classes, and it
    only works if the property names are identical
    string literals (we're at the mercy of the linker
    for that).
    
    If we don't get lucky, we fall back to using
    the pspec pool as usual.

 gobject/gobject.c | 170 +++++++++++++++++++++++++++++++++++++++---------------
 gobject/gobject.h |   6 +-
 2 files changed, 130 insertions(+), 46 deletions(-)
---
diff --git a/gobject/gobject.c b/gobject/gobject.c
index 2ee0db79c1..204700ca31 100644
--- a/gobject/gobject.c
+++ b/gobject/gobject.c
@@ -485,6 +485,8 @@ g_object_base_class_init (GObjectClass *class)
   class->n_construct_properties = g_slist_length (class->construct_properties);
   class->get_property = NULL;
   class->set_property = NULL;
+  class->pspecs = NULL;
+  class->n_pspecs = 0;
 }
 
 static void
@@ -704,6 +706,73 @@ g_object_class_install_property (GObjectClass *class,
                                               pspec);
 }
 
+typedef struct {
+  const char *name;
+  GParamSpec *pspec;
+} PspecEntry;
+
+static int
+compare_pspec_entry (const void *a,
+                     const void *b)
+{
+  const PspecEntry *ae = a;
+  const PspecEntry *be = b;
+
+  return ae->name < be->name ? -1 : (ae->name > be->name ? 1 : 0);
+}
+
+/* This uses pointer comparisons with @property_name, so
+ * will only work with string literals. */
+static inline GParamSpec *
+find_pspec (GObjectClass *class,
+            const char   *property_name)
+{
+  const PspecEntry *pspecs = (const PspecEntry *)class->pspecs;
+  gsize n_pspecs = class->n_pspecs;
+
+  /* The limit for choosing between linear and binary search is
+   * fairly arbitrary.
+   *
+   * Both searches use pointer comparisons against @property_name.
+   * If this function is called with a non-static @property_name,
+   * it will fall through to the g_param_spec_pool_lookup() case.
+   * That’s OK; this is an opportunistic optimisation which relies
+   * on the fact that *most* (but not all) property lookups use
+   * static property names.
+   */
+  if (n_pspecs < 10)
+    {
+      for (gsize i = 0; i < n_pspecs; i++)
+        {
+          if (pspecs[i].name == property_name)
+            return pspecs[i].pspec;
+        }
+    }
+  else
+    {
+      int lower = 0;
+      int upper = (int)class->n_pspecs - 1;
+      int mid;
+
+      while (lower <= upper)
+        {
+          mid = (lower + upper) / 2;
+
+          if (property_name < pspecs[mid].name)
+            upper = mid - 1;
+          else if (property_name > pspecs[mid].name)
+            lower = mid + 1;
+          else
+            return pspecs[mid].pspec;
+        }
+    }
+
+  return g_param_spec_pool_lookup (pspec_pool,
+                                   property_name,
+                                   ((GTypeClass *)class)->g_type,
+                                   TRUE);
+}
+
 /**
  * g_object_class_install_properties:
  * @oclass: a #GObjectClass
@@ -810,6 +879,29 @@ g_object_class_install_properties (GObjectClass  *oclass,
           break;
         }
     }
+
+  /* Save a copy of the pspec array inside the class struct. This
+   * makes it faster to look up pspecs for the class in future when
+   * acting on those properties.
+   *
+   * If a pspec is not in this cache array, calling code will fall
+   * back to using g_param_spec_pool_lookup(), so a pspec not being
+   * in this array is a (potential) performance problem but not a
+   * correctness problem. */
+  if (oclass->pspecs == NULL)
+    {
+      PspecEntry *entries = g_new (PspecEntry, n_pspecs - 1);
+      for (i = 1; i < n_pspecs; i++)
+        {
+          entries[i - 1].name = pspecs[i]->name;
+          entries[i - 1].pspec = pspecs[i];
+        }
+
+      qsort (entries, n_pspecs - 1, sizeof (PspecEntry), compare_pspec_entry);
+
+      oclass->pspecs = entries;
+      oclass->n_pspecs = n_pspecs - 1;
+    }
 }
 
 /**
@@ -872,25 +964,16 @@ g_object_class_find_property (GObjectClass *class,
                              const gchar  *property_name)
 {
   GParamSpec *pspec;
-  GParamSpec *redirect;
-       
+
   g_return_val_if_fail (G_IS_OBJECT_CLASS (class), NULL);
   g_return_val_if_fail (property_name != NULL, NULL);
-  
-  pspec = g_param_spec_pool_lookup (pspec_pool,
-                                   property_name,
-                                   G_OBJECT_CLASS_TYPE (class),
-                                   TRUE);
-  if (pspec)
-    {
-      redirect = g_param_spec_get_redirect_target (pspec);
-      if (redirect)
-       return redirect;
-      else
-       return pspec;
-    }
-  else
-    return NULL;
+
+  pspec = find_pspec (class, property_name);
+
+  if (pspec && ((GTypeInstance *)pspec)->g_class->g_type == G_TYPE_PARAM_OVERRIDE)
+    pspec = ((GParamSpecOverride *)pspec)->overridden;
+
+  return pspec;
 }
 
 /**
@@ -2217,8 +2300,8 @@ g_object_new_with_properties (GType          object_type,
       params = g_newa (GObjectConstructParam, n_properties);
       for (i = 0; i < n_properties; i++)
         {
-          GParamSpec *pspec;
-          pspec = g_param_spec_pool_lookup (pspec_pool, names[i], object_type, TRUE);
+          GParamSpec *pspec = find_pspec (class, names[i]);
+
           if (!g_object_new_is_valid_property (object_type, pspec, names[i], params, count))
             continue;
           params[count].pspec = pspec;
@@ -2283,9 +2366,8 @@ g_object_newv (GType       object_type,
 
       for (i = 0; i < n_parameters; i++)
         {
-          GParamSpec *pspec;
+          GParamSpec *pspec = find_pspec (class, parameters[i].name);
 
-          pspec = g_param_spec_pool_lookup (pspec_pool, parameters[i].name, object_type, TRUE);
           if (!g_object_new_is_valid_property (object_type, pspec, parameters[i].name, cparams, j))
             continue;
 
@@ -2356,9 +2438,7 @@ g_object_new_valist (GType        object_type,
       do
         {
           gchar *error = NULL;
-          GParamSpec *pspec;
-
-          pspec = g_param_spec_pool_lookup (pspec_pool, name, object_type, TRUE);
+          GParamSpec *pspec = find_pspec (class, name);
 
           if (!g_object_new_is_valid_property (object_type, pspec, name, params, n_params))
             break;
@@ -2524,7 +2604,7 @@ g_object_setv (GObject       *object,
   guint i;
   GObjectNotifyQueue *nqueue = NULL;
   GParamSpec *pspec;
-  GType obj_type;
+  GObjectClass *class;
 
   g_return_if_fail (G_IS_OBJECT (object));
 
@@ -2532,14 +2612,15 @@ g_object_setv (GObject       *object,
     return;
 
   g_object_ref (object);
-  obj_type = G_OBJECT_TYPE (object);
+
+  class = G_OBJECT_GET_CLASS (object);
 
   if (_g_object_has_notify_handler (object))
     nqueue = g_object_notify_queue_freeze (object, FALSE);
 
   for (i = 0; i < n_properties; i++)
     {
-      pspec = g_param_spec_pool_lookup (pspec_pool, names[i], obj_type, TRUE);
+      pspec = find_pspec (class, names[i]);
 
       if (!g_object_set_is_valid_property (object, pspec, names[i]))
         break;
@@ -2569,6 +2650,7 @@ g_object_set_valist (GObject       *object,
 {
   GObjectNotifyQueue *nqueue = NULL;
   const gchar *name;
+  GObjectClass *class;
   
   g_return_if_fail (G_IS_OBJECT (object));
 
@@ -2577,6 +2659,8 @@ g_object_set_valist (GObject       *object,
   if (_g_object_has_notify_handler (object))
     nqueue = g_object_notify_queue_freeze (object, FALSE);
 
+  class = G_OBJECT_GET_CLASS (object);
+
   name = first_property_name;
   while (name)
     {
@@ -2585,10 +2669,7 @@ g_object_set_valist (GObject      *object,
       gchar *error = NULL;
       GTypeValueTable *vtab;
       
-      pspec = g_param_spec_pool_lookup (pspec_pool,
-                                       name,
-                                       G_OBJECT_TYPE (object),
-                                       TRUE);
+      pspec = find_pspec (class, name);
 
       if (!g_object_set_is_valid_property (object, pspec, name))
         break;
@@ -2661,7 +2742,7 @@ g_object_getv (GObject      *object,
 {
   guint i;
   GParamSpec *pspec;
-  GType obj_type;
+  GObjectClass *class;
 
   g_return_if_fail (G_IS_OBJECT (object));
 
@@ -2670,12 +2751,14 @@ g_object_getv (GObject      *object,
 
   g_object_ref (object);
 
+  class = G_OBJECT_GET_CLASS (object);
+
   memset (values, 0, n_properties * sizeof (GValue));
 
-  obj_type = G_OBJECT_TYPE (object);
   for (i = 0; i < n_properties; i++)
     {
-      pspec = g_param_spec_pool_lookup (pspec_pool, names[i], obj_type, TRUE);
+      pspec = find_pspec (class, names[i]);
+
       if (!g_object_get_is_valid_property (object, pspec, names[i]))
         break;
       g_value_init (&values[i], pspec->value_type);
@@ -2705,23 +2788,23 @@ g_object_get_valist (GObject     *object,
                     va_list      var_args)
 {
   const gchar *name;
+  GObjectClass *class;
   
   g_return_if_fail (G_IS_OBJECT (object));
   
   g_object_ref (object);
-  
+
+  class = G_OBJECT_GET_CLASS (object);
+
   name = first_property_name;
-  
+
   while (name)
     {
       GValue value = G_VALUE_INIT;
       GParamSpec *pspec;
       gchar *error;
-      
-      pspec = g_param_spec_pool_lookup (pspec_pool,
-                                       name,
-                                       G_OBJECT_TYPE (object),
-                                       TRUE);
+
+      pspec = find_pspec (class, name);
 
       if (!g_object_get_is_valid_property (object, pspec, name))
         break;
@@ -2881,10 +2964,7 @@ g_object_get_property (GObject      *object,
   
   g_object_ref (object);
   
-  pspec = g_param_spec_pool_lookup (pspec_pool,
-                                   property_name,
-                                   G_OBJECT_TYPE (object),
-                                   TRUE);
+  pspec = find_pspec (G_OBJECT_GET_CLASS (object), property_name);
 
   if (g_object_get_is_valid_property (object, pspec, property_name))
     {
diff --git a/gobject/gobject.h b/gobject/gobject.h
index c67794ae50..04796c00a5 100644
--- a/gobject/gobject.h
+++ b/gobject/gobject.h
@@ -370,8 +370,12 @@ struct  _GObjectClass
   gsize                flags;
 
   gsize         n_construct_properties;
+
+  gpointer pspecs;
+  gsize n_pspecs;
+
   /* padding */
-  gpointer     pdummy[5];
+  gpointer     pdummy[3];
 };
 
 /**


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