1  /* GObject - GLib Type, Object, Parameter and Signal Library
       2   * Copyright (C) 2009 Red Hat, Inc.
       3   * Copyright (C) 2022 Canonical Ltd.
       4   *
       5   * This library is free software; you can redistribute it and/or
       6   * modify it under the terms of the GNU Lesser General Public
       7   * License as published by the Free Software Foundation; either
       8   * version 2.1 of the License, or (at your option) any later version.
       9   *
      10   * This library is distributed in the hope that it will be useful,
      11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13   * Lesser General Public License for more details.
      14   *
      15   * You should have received a copy of the GNU Lesser General
      16   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      17   */
      18  
      19  #include <math.h>
      20  #include <string.h>
      21  #include <glib-object.h>
      22  #include "../testcommon.h"
      23  
      24  #define WARM_UP_N_RUNS 50
      25  #define ESTIMATE_ROUND_TIME_N_RUNS 5
      26  #define DEFAULT_TEST_TIME 15 /* seconds */
      27   /* The time we want each round to take, in seconds, this should
      28    * be large enough compared to the timer resolution, but small
      29    * enough that the risk of any random slowness will miss the
      30    * running window */
      31  #define TARGET_ROUND_TIME 0.008
      32  
      33  static gboolean verbose = FALSE;
      34  static int test_length = DEFAULT_TEST_TIME;
      35  
      36  static GOptionEntry cmd_entries[] = {
      37    {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
      38     "Print extra information", NULL},
      39    {"seconds", 's', 0, G_OPTION_ARG_INT, &test_length,
      40     "Time to run each test in seconds", NULL},
      41    G_OPTION_ENTRY_NULL
      42  };
      43  
      44  typedef struct _PerformanceTest PerformanceTest;
      45  struct _PerformanceTest {
      46    const char *name;
      47    gpointer extra_data;
      48  
      49    gpointer (*setup) (PerformanceTest *test);
      50    void (*init) (PerformanceTest *test,
      51  		gpointer data,
      52  		double factor);
      53    void (*run) (PerformanceTest *test,
      54  	       gpointer data);
      55    void (*finish) (PerformanceTest *test,
      56  		  gpointer data);
      57    void (*teardown) (PerformanceTest *test,
      58  		    gpointer data);
      59    void (*print_result) (PerformanceTest *test,
      60  			gpointer data,
      61  			double time);
      62  };
      63  
      64  static void
      65  run_test (PerformanceTest *test)
      66  {
      67    gpointer data = NULL;
      68    guint64 i, num_rounds;
      69    double elapsed, min_elapsed, max_elapsed, avg_elapsed, factor;
      70    GTimer *timer;
      71  
      72    g_print ("Running test %s\n", test->name);
      73  
      74    /* Set up test */
      75    timer = g_timer_new ();
      76    data = test->setup (test);
      77  
      78    if (verbose)
      79      g_print ("Warming up\n");
      80  
      81    g_timer_start (timer);
      82  
      83    /* Warm up the test by doing a few runs */
      84    for (i = 0; i < WARM_UP_N_RUNS; i++)
      85      {
      86        test->init (test, data, 1.0);
      87        test->run (test, data);
      88        test->finish (test, data);
      89      }
      90  
      91    g_timer_stop (timer);
      92    elapsed = g_timer_elapsed (timer, NULL);
      93  
      94    if (verbose)
      95      {
      96        g_print ("Warm up time: %.2f secs\n", elapsed);
      97        g_print ("Estimating round time\n");
      98      }
      99  
     100    /* Estimate time for one run by doing a few test rounds */
     101    min_elapsed = 0;
     102    for (i = 0; i < ESTIMATE_ROUND_TIME_N_RUNS; i++)
     103      {
     104        test->init (test, data, 1.0);
     105        g_timer_start (timer);
     106        test->run (test, data);
     107        g_timer_stop (timer);
     108        test->finish (test, data);
     109  
     110        elapsed = g_timer_elapsed (timer, NULL);
     111        if (i == 0)
     112  	min_elapsed = elapsed;
     113        else
     114  	min_elapsed = MIN (min_elapsed, elapsed);
     115      }
     116  
     117    factor = TARGET_ROUND_TIME / min_elapsed;
     118  
     119    if (verbose)
     120      g_print ("Uncorrected round time: %.4f msecs, correction factor %.2f\n", 1000*min_elapsed, factor);
     121  
     122    /* Calculate number of rounds needed */
     123    num_rounds = (test_length / TARGET_ROUND_TIME) + 1;
     124  
     125    if (verbose)
     126      g_print ("Running %"G_GINT64_MODIFIER"d rounds\n", num_rounds);
     127  
     128    /* Run the test */
     129    avg_elapsed = 0.0;
     130    min_elapsed = 0.0;
     131    max_elapsed = 0.0;
     132    for (i = 0; i < num_rounds; i++)
     133      {
     134        test->init (test, data, factor);
     135        g_timer_start (timer);
     136        test->run (test, data);
     137        g_timer_stop (timer);
     138        test->finish (test, data);
     139        elapsed = g_timer_elapsed (timer, NULL);
     140  
     141        if (i == 0)
     142  	max_elapsed = min_elapsed = avg_elapsed = elapsed;
     143        else
     144          {
     145            min_elapsed = MIN (min_elapsed, elapsed);
     146            max_elapsed = MAX (max_elapsed, elapsed);
     147            avg_elapsed += elapsed;
     148          }
     149      }
     150  
     151    if (num_rounds > 1)
     152      avg_elapsed = avg_elapsed / num_rounds;
     153  
     154    if (verbose)
     155      {
     156        g_print ("Minimum corrected round time: %.2f msecs\n", min_elapsed * 1000);
     157        g_print ("Maximum corrected round time: %.2f msecs\n", max_elapsed * 1000);
     158        g_print ("Average corrected round time: %.2f msecs\n", avg_elapsed * 1000);
     159      }
     160  
     161    /* Print the results */
     162    test->print_result (test, data, min_elapsed);
     163  
     164    /* Tear down */
     165    test->teardown (test, data);
     166    g_timer_destroy (timer);
     167  }
     168  
     169  /*************************************************************
     170   * Simple object is a very simple small GObject subclass
     171   * with no properties, no signals, implementing no interfaces
     172   *************************************************************/
     173  
     174  static GType simple_object_get_type (void);
     175  #define SIMPLE_TYPE_OBJECT        (simple_object_get_type ())
     176  typedef struct _SimpleObject      SimpleObject;
     177  typedef struct _SimpleObjectClass   SimpleObjectClass;
     178  
     179  struct _SimpleObject
     180  {
     181    GObject parent_instance;
     182    int val;
     183  };
     184  
     185  struct _SimpleObjectClass
     186  {
     187    GObjectClass parent_class;
     188  };
     189  
     190  G_DEFINE_TYPE (SimpleObject, simple_object, G_TYPE_OBJECT)
     191  
     192  static void
     193  simple_object_finalize (GObject *object)
     194  {
     195    G_OBJECT_CLASS (simple_object_parent_class)->finalize (object);
     196  }
     197  
     198  static void
     199  simple_object_class_init (SimpleObjectClass *class)
     200  {
     201    GObjectClass *object_class = G_OBJECT_CLASS (class);
     202  
     203    object_class->finalize = simple_object_finalize;
     204  }
     205  
     206  static void
     207  simple_object_init (SimpleObject *simple_object)
     208  {
     209    simple_object->val = 42;
     210  }
     211  
     212  typedef struct _TestIfaceClass TestIfaceClass;
     213  typedef struct _TestIfaceClass TestIface1Class;
     214  typedef struct _TestIfaceClass TestIface2Class;
     215  typedef struct _TestIfaceClass TestIface3Class;
     216  typedef struct _TestIfaceClass TestIface4Class;
     217  typedef struct _TestIfaceClass TestIface5Class;
     218  typedef struct _TestIface TestIface;
     219  
     220  struct _TestIfaceClass
     221  {
     222    GTypeInterface base_iface;
     223    void (*method) (TestIface *obj);
     224  };
     225  
     226  static GType test_iface1_get_type (void);
     227  static GType test_iface2_get_type (void);
     228  static GType test_iface3_get_type (void);
     229  static GType test_iface4_get_type (void);
     230  static GType test_iface5_get_type (void);
     231  
     232  #define TEST_TYPE_IFACE1 (test_iface1_get_type ())
     233  #define TEST_TYPE_IFACE2 (test_iface2_get_type ())
     234  #define TEST_TYPE_IFACE3 (test_iface3_get_type ())
     235  #define TEST_TYPE_IFACE4 (test_iface4_get_type ())
     236  #define TEST_TYPE_IFACE5 (test_iface5_get_type ())
     237  
     238  static DEFINE_IFACE (TestIface1, test_iface1,  NULL, NULL)
     239  static DEFINE_IFACE (TestIface2, test_iface2,  NULL, NULL)
     240  static DEFINE_IFACE (TestIface3, test_iface3,  NULL, NULL)
     241  static DEFINE_IFACE (TestIface4, test_iface4,  NULL, NULL)
     242  static DEFINE_IFACE (TestIface5, test_iface5,  NULL, NULL)
     243  
     244  /*************************************************************
     245   * Complex object is a GObject subclass with a properties,
     246   * construct properties, signals and implementing an interface.
     247   *************************************************************/
     248  
     249  static GType complex_object_get_type (void);
     250  #define COMPLEX_TYPE_OBJECT        (complex_object_get_type ())
     251  typedef struct _ComplexObject      ComplexObject;
     252  typedef struct _ComplexObjectClass ComplexObjectClass;
     253  
     254  struct _ComplexObject
     255  {
     256    GObject parent_instance;
     257    int val1;
     258    char *val2;
     259  };
     260  
     261  struct _ComplexObjectClass
     262  {
     263    GObjectClass parent_class;
     264  
     265    void (*signal) (ComplexObject *obj);
     266    void (*signal_empty) (ComplexObject *obj);
     267  };
     268  
     269  static void complex_test_iface_init (gpointer         g_iface,
     270  				     gpointer         iface_data);
     271  
     272  G_DEFINE_TYPE_EXTENDED (ComplexObject, complex_object,
     273  			G_TYPE_OBJECT, 0,
     274  			G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE1, complex_test_iface_init)
     275  			G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE2, complex_test_iface_init)
     276  			G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE3, complex_test_iface_init)
     277  			G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE4, complex_test_iface_init)
     278  			G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE5, complex_test_iface_init))
     279  
     280  #define COMPLEX_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), COMPLEX_TYPE_OBJECT, ComplexObject))
     281  
     282  enum {
     283    PROP_0,
     284    PROP_VAL1,
     285    PROP_VAL2,
     286    N_PROPERTIES
     287  };
     288  
     289  static GParamSpec *pspecs[N_PROPERTIES] = { NULL, };
     290  
     291  enum {
     292    COMPLEX_SIGNAL,
     293    COMPLEX_SIGNAL_EMPTY,
     294    COMPLEX_SIGNAL_GENERIC,
     295    COMPLEX_SIGNAL_GENERIC_EMPTY,
     296    COMPLEX_SIGNAL_ARGS,
     297    COMPLEX_LAST_SIGNAL
     298  };
     299  
     300  static guint complex_signals[COMPLEX_LAST_SIGNAL] = { 0 };
     301  
     302  static void
     303  complex_object_finalize (GObject *object)
     304  {
     305    ComplexObject *c = COMPLEX_OBJECT (object);
     306  
     307    g_free (c->val2);
     308  
     309    G_OBJECT_CLASS (complex_object_parent_class)->finalize (object);
     310  }
     311  
     312  static void
     313  complex_object_set_property (GObject         *object,
     314  			     guint            prop_id,
     315  			     const GValue    *value,
     316  			     GParamSpec      *pspec)
     317  {
     318    ComplexObject *complex = COMPLEX_OBJECT (object);
     319  
     320    switch (prop_id)
     321      {
     322      case PROP_VAL1:
     323        complex->val1 = g_value_get_int (value);
     324        break;
     325      case PROP_VAL2:
     326        g_free (complex->val2);
     327        complex->val2 = g_value_dup_string (value);
     328        break;
     329      default:
     330        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     331        break;
     332      }
     333  }
     334  
     335  static void
     336  complex_object_get_property (GObject         *object,
     337  			     guint            prop_id,
     338  			     GValue          *value,
     339  			     GParamSpec      *pspec)
     340  {
     341    ComplexObject *complex = COMPLEX_OBJECT (object);
     342  
     343    switch (prop_id)
     344      {
     345      case PROP_VAL1:
     346        g_value_set_int (value, complex->val1);
     347        break;
     348      case PROP_VAL2:
     349        g_value_set_string (value, complex->val2);
     350        break;
     351      default:
     352        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     353        break;
     354      }
     355  }
     356  
     357  static void
     358  complex_object_real_signal (ComplexObject *obj)
     359  {
     360  }
     361  
     362  static void
     363  complex_object_class_init (ComplexObjectClass *class)
     364  {
     365    GObjectClass *object_class = G_OBJECT_CLASS (class);
     366  
     367    object_class->finalize = complex_object_finalize;
     368    object_class->set_property = complex_object_set_property;
     369    object_class->get_property = complex_object_get_property;
     370  
     371    class->signal = complex_object_real_signal;
     372  
     373    complex_signals[COMPLEX_SIGNAL] =
     374      g_signal_new ("signal",
     375  		  G_TYPE_FROM_CLASS (object_class),
     376  		  G_SIGNAL_RUN_FIRST,
     377  		  G_STRUCT_OFFSET (ComplexObjectClass, signal),
     378  		  NULL, NULL,
     379  		  g_cclosure_marshal_VOID__VOID,
     380  		  G_TYPE_NONE, 0);
     381  
     382    complex_signals[COMPLEX_SIGNAL_EMPTY] =
     383      g_signal_new ("signal-empty",
     384  		  G_TYPE_FROM_CLASS (object_class),
     385  		  G_SIGNAL_RUN_FIRST,
     386  		  G_STRUCT_OFFSET (ComplexObjectClass, signal_empty),
     387  		  NULL, NULL,
     388  		  g_cclosure_marshal_VOID__VOID,
     389  		  G_TYPE_NONE, 0);
     390  
     391    complex_signals[COMPLEX_SIGNAL_GENERIC] =
     392      g_signal_new ("signal-generic",
     393  		  G_TYPE_FROM_CLASS (object_class),
     394  		  G_SIGNAL_RUN_FIRST,
     395  		  G_STRUCT_OFFSET (ComplexObjectClass, signal),
     396  		  NULL, NULL,
     397  		  NULL,
     398  		  G_TYPE_NONE, 0);
     399    complex_signals[COMPLEX_SIGNAL_GENERIC_EMPTY] =
     400      g_signal_new ("signal-generic-empty",
     401  		  G_TYPE_FROM_CLASS (object_class),
     402  		  G_SIGNAL_RUN_FIRST,
     403  		  G_STRUCT_OFFSET (ComplexObjectClass, signal_empty),
     404  		  NULL, NULL,
     405  		  NULL,
     406  		  G_TYPE_NONE, 0);
     407  
     408    complex_signals[COMPLEX_SIGNAL_ARGS] =
     409      g_signal_new ("signal-args",
     410                    G_TYPE_FROM_CLASS (object_class),
     411                    G_SIGNAL_RUN_FIRST,
     412                    G_STRUCT_OFFSET (ComplexObjectClass, signal),
     413                    NULL, NULL,
     414                    g_cclosure_marshal_VOID__UINT_POINTER,
     415                    G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
     416  
     417    pspecs[PROP_VAL1] = g_param_spec_int ("val1", "val1", "val1",
     418                                          0, G_MAXINT, 42,
     419                                          G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
     420    pspecs[PROP_VAL2] = g_param_spec_string ("val2", "val2", "val2",
     421                                             NULL,
     422                                             G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
     423  
     424    g_object_class_install_properties (object_class, N_PROPERTIES, pspecs);
     425  }
     426  
     427  static void
     428  complex_object_iface_method (TestIface *obj)
     429  {
     430    ComplexObject *complex = COMPLEX_OBJECT (obj);
     431    complex->val1++;
     432  }
     433  
     434  static void
     435  complex_test_iface_init (gpointer         g_iface,
     436  			 gpointer         iface_data)
     437  {
     438    TestIfaceClass *iface = g_iface;
     439    iface->method = complex_object_iface_method;
     440  }
     441  
     442  static void
     443  complex_object_init (ComplexObject *complex_object)
     444  {
     445    complex_object->val1 = 42;
     446  }
     447  
     448  /*************************************************************
     449   * Test object construction performance
     450   *************************************************************/
     451  
     452  #define NUM_OBJECT_TO_CONSTRUCT 10000
     453  
     454  struct ConstructionTest {
     455    GObject **objects;
     456    int n_objects;
     457    GType type;
     458  };
     459  
     460  static gpointer
     461  test_construction_setup (PerformanceTest *test)
     462  {
     463    struct ConstructionTest *data;
     464  
     465    data = g_new0 (struct ConstructionTest, 1);
     466    data->type = ((GType (*)(void))test->extra_data)();
     467  
     468    return data;
     469  }
     470  
     471  static void
     472  test_construction_init (PerformanceTest *test,
     473  			gpointer _data,
     474  			double count_factor)
     475  {
     476    struct ConstructionTest *data = _data;
     477    int n;
     478  
     479    n = NUM_OBJECT_TO_CONSTRUCT * count_factor;
     480    if (data->n_objects != n)
     481      {
     482        data->n_objects = n;
     483        data->objects = g_renew (GObject *, data->objects, n);
     484      }
     485  }
     486  
     487  static void
     488  test_construction_run (PerformanceTest *test,
     489  		       gpointer _data)
     490  {
     491    struct ConstructionTest *data = _data;
     492    GObject **objects = data->objects;
     493    GType type = data->type;
     494    int i, n_objects;
     495  
     496    n_objects = data->n_objects;
     497    for (i = 0; i < n_objects; i++)
     498      objects[i] = g_object_new (type, NULL);
     499  }
     500  
     501  static void
     502  test_construction_run1 (PerformanceTest *test,
     503  		        gpointer _data)
     504  {
     505    struct ConstructionTest *data = _data;
     506    GObject **objects = data->objects;
     507    int i, n_objects;
     508  
     509    n_objects = data->n_objects;
     510    for (i = 0; i < n_objects; i++)
     511      objects[i] = (GObject *) g_slice_new0 (SimpleObject);
     512  }
     513  
     514  static void
     515  test_complex_construction_run (PerformanceTest *test,
     516                                 gpointer _data)
     517  {
     518    struct ConstructionTest *data = _data;
     519    GObject **objects = data->objects;
     520    GType type = data->type;
     521    int i, n_objects;
     522  
     523    n_objects = data->n_objects;
     524    for (i = 0; i < n_objects; i++)
     525      objects[i] = g_object_new (type, "val1", 5, "val2", "thousand", NULL);
     526  }
     527  
     528  static void
     529  test_complex_construction_run1 (PerformanceTest *test,
     530                                  gpointer _data)
     531  {
     532    struct ConstructionTest *data = _data;
     533    GObject **objects = data->objects;
     534    GType type = data->type;
     535    int i, n_objects;
     536  
     537    n_objects = data->n_objects;
     538    for (i = 0; i < n_objects; i++)
     539      {
     540        ComplexObject *object;
     541        object = (ComplexObject *)g_object_new (type, NULL);
     542        object->val1 = 5;
     543        object->val2 = g_strdup ("thousand");
     544        objects[i] = (GObject *)object;
     545      }
     546  }
     547  
     548  static void
     549  test_complex_construction_run2 (PerformanceTest *test,
     550                                  gpointer _data)
     551  {
     552    struct ConstructionTest *data = _data;
     553    GObject **objects = data->objects;
     554    GType type = data->type;
     555    int i, n_objects;
     556  
     557    n_objects = data->n_objects;
     558    for (i = 0; i < n_objects; i++)
     559      {
     560        objects[i] = g_object_new (type, NULL);
     561      }
     562  }
     563  
     564  static void
     565  test_construction_finish (PerformanceTest *test,
     566  			  gpointer _data)
     567  {
     568    struct ConstructionTest *data = _data;
     569    int i;
     570  
     571    for (i = 0; i < data->n_objects; i++)
     572      g_object_unref (data->objects[i]);
     573  }
     574  
     575  static void
     576  test_construction_finish1 (PerformanceTest *test,
     577  			   gpointer _data)
     578  {
     579    struct ConstructionTest *data = _data;
     580    int i;
     581  
     582    for (i = 0; i < data->n_objects; i++)
     583      g_slice_free (SimpleObject, (SimpleObject *)data->objects[i]);
     584  }
     585  
     586  static void
     587  test_construction_teardown (PerformanceTest *test,
     588  			    gpointer _data)
     589  {
     590    struct ConstructionTest *data = _data;
     591    g_free (data->objects);
     592    g_free (data);
     593  }
     594  
     595  static void
     596  test_finalization_init (PerformanceTest *test,
     597  			gpointer _data,
     598  			double count_factor)
     599  {
     600    struct ConstructionTest *data = _data;
     601    int n;
     602  
     603    n = NUM_OBJECT_TO_CONSTRUCT * count_factor;
     604    if (data->n_objects != n)
     605      {
     606        data->n_objects = n;
     607        data->objects = g_renew (GObject *, data->objects, n);
     608      }
     609  
     610    for (int i = 0; i <  data->n_objects; i++)
     611      {
     612        data->objects[i] = g_object_new (data->type, NULL);
     613      }
     614  }
     615  
     616  static void
     617  test_finalization_run (PerformanceTest *test,
     618  		       gpointer _data)
     619  {
     620    struct ConstructionTest *data = _data;
     621    GObject **objects = data->objects;
     622    int i, n_objects;
     623  
     624    n_objects = data->n_objects;
     625    for (i = 0; i < n_objects; i++)
     626      {
     627        g_object_unref (objects[i]);
     628      }
     629  }
     630  
     631  static void
     632  test_finalization_finish (PerformanceTest *test,
     633  			  gpointer _data)
     634  {
     635  }
     636  
     637  static void
     638  test_construction_print_result (PerformanceTest *test,
     639  				gpointer _data,
     640  				double time)
     641  {
     642    struct ConstructionTest *data = _data;
     643  
     644    g_print ("Millions of constructed objects per second: %.3f\n",
     645  	   data->n_objects / (time * 1000000));
     646  }
     647  
     648  static void
     649  test_finalization_print_result (PerformanceTest *test,
     650  				gpointer _data,
     651  				double time)
     652  {
     653    struct ConstructionTest *data = _data;
     654  
     655    g_print ("Millions of finalized objects per second: %.3f\n",
     656  	   data->n_objects / (time * 1000000));
     657  }
     658  
     659  /*************************************************************
     660   * Test runtime type check performance
     661   *************************************************************/
     662  
     663  #define NUM_KILO_CHECKS_PER_ROUND 50
     664  
     665  struct TypeCheckTest {
     666    GObject *object;
     667    int n_checks;
     668  };
     669  
     670  static gpointer
     671  test_type_check_setup (PerformanceTest *test)
     672  {
     673    struct TypeCheckTest *data;
     674  
     675    data = g_new0 (struct TypeCheckTest, 1);
     676    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
     677  
     678    return data;
     679  }
     680  
     681  static void
     682  test_type_check_init (PerformanceTest *test,
     683  		      gpointer _data,
     684  		      double factor)
     685  {
     686    struct TypeCheckTest *data = _data;
     687  
     688    data->n_checks = factor * NUM_KILO_CHECKS_PER_ROUND;
     689  }
     690  
     691  
     692  /* Work around g_type_check_instance_is_a being marked "pure",
     693     and thus only called once for the loop. */
     694  gboolean (*my_type_check_instance_is_a) (GTypeInstance *type_instance,
     695  					 GType          iface_type) = &g_type_check_instance_is_a;
     696  
     697  static void
     698  test_type_check_run (PerformanceTest *test,
     699  		     gpointer _data)
     700  {
     701    struct TypeCheckTest *data = _data;
     702    GObject *object = data->object;
     703    GType type, types[5];
     704    int i, j;
     705  
     706    types[0] = test_iface1_get_type ();
     707    types[1] = test_iface2_get_type ();
     708    types[2] = test_iface3_get_type ();
     709    types[3] = test_iface4_get_type ();
     710    types[4] = test_iface5_get_type ();
     711  
     712    for (i = 0; i < data->n_checks; i++)
     713      {
     714        type = types[i%5];
     715        for (j = 0; j < 1000; j++)
     716  	{
     717  	  my_type_check_instance_is_a ((GTypeInstance *)object,
     718  				       type);
     719  	}
     720      }
     721  }
     722  
     723  static void
     724  test_type_check_finish (PerformanceTest *test,
     725  			gpointer data)
     726  {
     727  }
     728  
     729  static void
     730  test_type_check_print_result (PerformanceTest *test,
     731  			      gpointer _data,
     732  			      double time)
     733  {
     734    struct TypeCheckTest *data = _data;
     735    g_print ("Million type checks per second: %.2f\n",
     736  	   data->n_checks / (1000*time));
     737  }
     738  
     739  static void
     740  test_type_check_teardown (PerformanceTest *test,
     741  			  gpointer _data)
     742  {
     743    struct TypeCheckTest *data = _data;
     744  
     745    g_object_unref (data->object);
     746    g_free (data);
     747  }
     748  
     749  /*************************************************************
     750   * Test signal emissions performance (common code)
     751   *************************************************************/
     752  
     753  #define NUM_EMISSIONS_PER_ROUND 10000
     754  
     755  struct EmissionTest {
     756    GObject *object;
     757    int n_checks;
     758    int signal_id;
     759  };
     760  
     761  static void
     762  test_emission_run (PerformanceTest *test,
     763                               gpointer _data)
     764  {
     765    struct EmissionTest *data = _data;
     766    GObject *object = data->object;
     767    int i;
     768  
     769    for (i = 0; i < data->n_checks; i++)
     770      g_signal_emit (object, data->signal_id, 0);
     771  }
     772  
     773  static void
     774  test_emission_run_args (PerformanceTest *test,
     775                          gpointer _data)
     776  {
     777    struct EmissionTest *data = _data;
     778    GObject *object = data->object;
     779    int i;
     780  
     781    for (i = 0; i < data->n_checks; i++)
     782      g_signal_emit (object, data->signal_id, 0, 0, NULL);
     783  }
     784  
     785  /*************************************************************
     786   * Test signal unhandled emissions performance
     787   *************************************************************/
     788  
     789  static gpointer
     790  test_emission_unhandled_setup (PerformanceTest *test)
     791  {
     792    struct EmissionTest *data;
     793  
     794    data = g_new0 (struct EmissionTest, 1);
     795    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
     796    data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)];
     797    return data;
     798  }
     799  
     800  static void
     801  test_emission_unhandled_init (PerformanceTest *test,
     802                                gpointer _data,
     803                                double factor)
     804  {
     805    struct EmissionTest *data = _data;
     806  
     807    data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
     808  }
     809  
     810  static void
     811  test_emission_unhandled_finish (PerformanceTest *test,
     812                                  gpointer data)
     813  {
     814  }
     815  
     816  static void
     817  test_emission_unhandled_print_result (PerformanceTest *test,
     818                                        gpointer _data,
     819                                        double time)
     820  {
     821    struct EmissionTest *data = _data;
     822  
     823    g_print ("Emissions per second: %.0f\n",
     824  	   data->n_checks / time);
     825  }
     826  
     827  static void
     828  test_emission_unhandled_teardown (PerformanceTest *test,
     829                                    gpointer _data)
     830  {
     831    struct EmissionTest *data = _data;
     832  
     833    g_object_unref (data->object);
     834    g_free (data);
     835  }
     836  
     837  /*************************************************************
     838   * Test signal handled emissions performance
     839   *************************************************************/
     840  
     841  static void
     842  test_emission_handled_handler (ComplexObject *obj, gpointer data)
     843  {
     844  }
     845  
     846  static gpointer
     847  test_emission_handled_setup (PerformanceTest *test)
     848  {
     849    struct EmissionTest *data;
     850  
     851    data = g_new0 (struct EmissionTest, 1);
     852    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
     853    data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)];
     854    g_signal_connect (data->object, "signal",
     855                      G_CALLBACK (test_emission_handled_handler),
     856                      NULL);
     857    g_signal_connect (data->object, "signal-empty",
     858                      G_CALLBACK (test_emission_handled_handler),
     859                      NULL);
     860    g_signal_connect (data->object, "signal-generic",
     861                      G_CALLBACK (test_emission_handled_handler),
     862                      NULL);
     863    g_signal_connect (data->object, "signal-generic-empty",
     864                      G_CALLBACK (test_emission_handled_handler),
     865                      NULL);
     866    g_signal_connect (data->object, "signal-args",
     867                      G_CALLBACK (test_emission_handled_handler),
     868                      NULL);
     869  
     870    return data;
     871  }
     872  
     873  static void
     874  test_emission_handled_init (PerformanceTest *test,
     875                              gpointer _data,
     876                              double factor)
     877  {
     878    struct EmissionTest *data = _data;
     879  
     880    data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
     881  }
     882  
     883  static void
     884  test_emission_handled_finish (PerformanceTest *test,
     885                                gpointer data)
     886  {
     887  }
     888  
     889  static void
     890  test_emission_handled_print_result (PerformanceTest *test,
     891                                      gpointer _data,
     892                                      double time)
     893  {
     894    struct EmissionTest *data = _data;
     895  
     896    g_print ("Emissions per second: %.0f\n",
     897  	   data->n_checks / time);
     898  }
     899  
     900  static void
     901  test_emission_handled_teardown (PerformanceTest *test,
     902                                  gpointer _data)
     903  {
     904    struct EmissionTest *data = _data;
     905  
     906    g_object_unref (data->object);
     907    g_free (data);
     908  }
     909  
     910  /*************************************************************
     911   * Test object notify performance (common code)
     912   *************************************************************/
     913  
     914  #define NUM_NOTIFY_PER_ROUND 10000
     915  
     916  struct NotifyTest {
     917    GObject *object;
     918    unsigned n_checks;
     919  };
     920  
     921  static void
     922  test_notify_run (PerformanceTest *test,
     923                   void *_data)
     924  {
     925    struct NotifyTest *data = _data;
     926    GObject *object = data->object;
     927  
     928    for (unsigned i = 0; i < data->n_checks; i++)
     929      g_object_notify (object, "val1");
     930  }
     931  
     932  static void
     933  test_notify_by_pspec_run (PerformanceTest *test,
     934                            void *_data)
     935  {
     936    struct NotifyTest *data = _data;
     937    GObject *object = data->object;
     938  
     939    for (unsigned i = 0; i < data->n_checks; i++)
     940      g_object_notify_by_pspec (object, pspecs[PROP_VAL1]);
     941  }
     942  
     943  /*************************************************************
     944   * Test notify unhandled performance
     945   *************************************************************/
     946  
     947  static void *
     948  test_notify_unhandled_setup (PerformanceTest *test)
     949  {
     950    struct NotifyTest *data;
     951  
     952    data = g_new0 (struct NotifyTest, 1);
     953    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
     954    return data;
     955  }
     956  
     957  static void
     958  test_notify_unhandled_init (PerformanceTest *test,
     959                              void *_data,
     960                              double factor)
     961  {
     962    struct NotifyTest *data = _data;
     963  
     964    data->n_checks = factor * NUM_NOTIFY_PER_ROUND;
     965  }
     966  
     967  static void
     968  test_notify_unhandled_finish (PerformanceTest *test,
     969                                void *data)
     970  {
     971  }
     972  
     973  static void
     974  test_notify_unhandled_print_result (PerformanceTest *test,
     975                                      void *_data,
     976                                      double time)
     977  {
     978    struct NotifyTest *data = _data;
     979  
     980    g_print ("Notify (unhandled) per second: %.0f\n",
     981             data->n_checks / time);
     982  }
     983  
     984  static void
     985  test_notify_unhandled_teardown (PerformanceTest *test,
     986                                  void *_data)
     987  {
     988    struct NotifyTest *data = _data;
     989  
     990    g_object_unref (data->object);
     991    g_free (data);
     992  }
     993  
     994  /*************************************************************
     995   * Test notify handled performance
     996   *************************************************************/
     997  
     998  static void
     999  test_notify_handled_handler (ComplexObject *obj, GParamSpec *pspec, void *data)
    1000  {
    1001  }
    1002  
    1003  static void *
    1004  test_notify_handled_setup (PerformanceTest *test)
    1005  {
    1006    struct NotifyTest *data;
    1007  
    1008    data = g_new0 (struct NotifyTest, 1);
    1009    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
    1010  
    1011    g_signal_connect (data->object, "notify::val1",
    1012                      G_CALLBACK (test_notify_handled_handler), data);
    1013    g_signal_connect (data->object, "notify::val2",
    1014                      G_CALLBACK (test_notify_handled_handler), data);
    1015  
    1016    return data;
    1017  }
    1018  
    1019  static void
    1020  test_notify_handled_init (PerformanceTest *test,
    1021                            void *_data,
    1022                            double factor)
    1023  {
    1024    struct NotifyTest *data = _data;
    1025  
    1026    data->n_checks = factor * NUM_NOTIFY_PER_ROUND;
    1027  }
    1028  
    1029  static void
    1030  test_notify_handled_finish (PerformanceTest *test,
    1031                              void *data)
    1032  {
    1033  }
    1034  
    1035  static void
    1036  test_notify_handled_print_result (PerformanceTest *test,
    1037                                    void *_data,
    1038                                    double time)
    1039  {
    1040    struct NotifyTest *data = _data;
    1041  
    1042    g_print ("Notify per second: %.0f\n",
    1043             data->n_checks / time);
    1044  }
    1045  
    1046  static void
    1047  test_notify_handled_teardown (PerformanceTest *test,
    1048                                void *_data)
    1049  {
    1050    struct NotifyTest *data = _data;
    1051  
    1052    g_assert_cmpuint (
    1053      g_signal_handlers_disconnect_by_func (data->object,
    1054                                            test_notify_handled_handler,
    1055                                            data), ==, 2);
    1056    g_object_unref (data->object);
    1057    g_free (data);
    1058  }
    1059  
    1060  /*************************************************************
    1061   * Test object set performance
    1062   *************************************************************/
    1063  
    1064  #define NUM_SET_PER_ROUND 10000
    1065  
    1066  struct SetTest {
    1067    GObject *object;
    1068    unsigned n_checks;
    1069  };
    1070  
    1071  static void
    1072  test_set_run (PerformanceTest *test,
    1073                void *_data)
    1074  {
    1075    struct SetTest *data = _data;
    1076    GObject *object = data->object;
    1077  
    1078    for (unsigned i = 0; i < data->n_checks; i++)
    1079      g_object_set (object, "val1", i, NULL);
    1080  }
    1081  
    1082  static void *
    1083  test_set_setup (PerformanceTest *test)
    1084  {
    1085    struct SetTest *data;
    1086  
    1087    data = g_new0 (struct SetTest, 1);
    1088    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
    1089  
    1090    return data;
    1091  }
    1092  
    1093  static void
    1094  test_set_init (PerformanceTest *test,
    1095                 void *_data,
    1096                 double factor)
    1097  {
    1098    struct SetTest *data = _data;
    1099  
    1100    data->n_checks = factor * NUM_SET_PER_ROUND;
    1101  }
    1102  
    1103  static void
    1104  test_set_finish (PerformanceTest *test,
    1105                   void *data)
    1106  {
    1107  }
    1108  
    1109  static void
    1110  test_set_print_result (PerformanceTest *test,
    1111                         void *_data,
    1112                         double time)
    1113  {
    1114    struct SetTest *data = _data;
    1115  
    1116    g_print ("Property set per second: %.0f\n",
    1117             data->n_checks / time);
    1118  }
    1119  
    1120  static void
    1121  test_set_teardown (PerformanceTest *test,
    1122                     void *_data)
    1123  {
    1124    struct SetTest *data = _data;
    1125  
    1126    g_object_unref (data->object);
    1127    g_free (data);
    1128  }
    1129  
    1130  /*************************************************************
    1131   * Test object get performance
    1132   *************************************************************/
    1133  
    1134  #define NUM_GET_PER_ROUND 10000
    1135  
    1136  struct GetTest {
    1137    GObject *object;
    1138    unsigned n_checks;
    1139  };
    1140  
    1141  static void
    1142  test_get_run (PerformanceTest *test,
    1143                void *_data)
    1144  {
    1145    struct GetTest *data = _data;
    1146    GObject *object = data->object;
    1147    int val;
    1148  
    1149    for (unsigned i = 0; i < data->n_checks; i++)
    1150      g_object_get (object, "val1", &val, NULL);
    1151  }
    1152  
    1153  static void *
    1154  test_get_setup (PerformanceTest *test)
    1155  {
    1156    struct GetTest *data;
    1157  
    1158    data = g_new0 (struct GetTest, 1);
    1159    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
    1160  
    1161    return data;
    1162  }
    1163  
    1164  static void
    1165  test_get_init (PerformanceTest *test,
    1166                 void *_data,
    1167                 double factor)
    1168  {
    1169    struct GetTest *data = _data;
    1170  
    1171    data->n_checks = factor * NUM_GET_PER_ROUND;
    1172  }
    1173  
    1174  static void
    1175  test_get_finish (PerformanceTest *test,
    1176                   void *data)
    1177  {
    1178  }
    1179  
    1180  static void
    1181  test_get_print_result (PerformanceTest *test,
    1182                         void *_data,
    1183                         double time)
    1184  {
    1185    struct GetTest *data = _data;
    1186  
    1187    g_print ("Property get per second: %.0f\n",
    1188             data->n_checks / time);
    1189  }
    1190  
    1191  static void
    1192  test_get_teardown (PerformanceTest *test,
    1193                     gpointer _data)
    1194  {
    1195    struct GetTest *data = _data;
    1196  
    1197    g_object_unref (data->object);
    1198    g_free (data);
    1199  }
    1200  
    1201  /*************************************************************
    1202   * Test object refcount performance
    1203   *************************************************************/
    1204  
    1205  #define NUM_KILO_REFS_PER_ROUND 100000
    1206  
    1207  struct RefcountTest {
    1208    GObject *object;
    1209    int n_checks;
    1210  };
    1211  
    1212  static gpointer
    1213  test_refcount_setup (PerformanceTest *test)
    1214  {
    1215    struct RefcountTest *data;
    1216  
    1217    data = g_new0 (struct RefcountTest, 1);
    1218    data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
    1219  
    1220    return data;
    1221  }
    1222  
    1223  static void
    1224  test_refcount_init (PerformanceTest *test,
    1225                      gpointer _data,
    1226                      double factor)
    1227  {
    1228    struct RefcountTest *data = _data;
    1229  
    1230    data->n_checks = factor * NUM_KILO_REFS_PER_ROUND;
    1231  }
    1232  
    1233  static void
    1234  test_refcount_run (PerformanceTest *test,
    1235                     gpointer _data)
    1236  {
    1237    struct RefcountTest *data = _data;
    1238    GObject *object = data->object;
    1239    int i;
    1240  
    1241    for (i = 0; i < data->n_checks; i++)
    1242      {
    1243        g_object_ref (object);
    1244        g_object_ref (object);
    1245        g_object_ref (object);
    1246        g_object_unref (object);
    1247        g_object_unref (object);
    1248  
    1249        g_object_ref (object);
    1250        g_object_ref (object);
    1251        g_object_unref (object);
    1252        g_object_unref (object);
    1253        g_object_unref (object);
    1254      }
    1255  }
    1256  
    1257  static void
    1258  test_refcount_finish (PerformanceTest *test,
    1259                        gpointer _data)
    1260  {
    1261  }
    1262  
    1263  static void
    1264  test_refcount_print_result (PerformanceTest *test,
    1265  			      gpointer _data,
    1266  			      double time)
    1267  {
    1268    struct RefcountTest *data = _data;
    1269    g_print ("Million refs+unref per second: %.2f\n",
    1270  	   data->n_checks * 5 / (time * 1000000 ));
    1271  }
    1272  
    1273  static void
    1274  test_refcount_teardown (PerformanceTest *test,
    1275  			  gpointer _data)
    1276  {
    1277    struct RefcountTest *data = _data;
    1278  
    1279    g_object_unref (data->object);
    1280    g_free (data);
    1281  }
    1282  
    1283  /*************************************************************
    1284   * Main test code
    1285   *************************************************************/
    1286  
    1287  static PerformanceTest tests[] = {
    1288    {
    1289      "simple-construction",
    1290      simple_object_get_type,
    1291      test_construction_setup,
    1292      test_construction_init,
    1293      test_construction_run,
    1294      test_construction_finish,
    1295      test_construction_teardown,
    1296      test_construction_print_result
    1297    },
    1298    {
    1299      "simple-construction1",
    1300      simple_object_get_type,
    1301      test_construction_setup,
    1302      test_construction_init,
    1303      test_construction_run1,
    1304      test_construction_finish1,
    1305      test_construction_teardown,
    1306      test_construction_print_result
    1307    },
    1308    {
    1309      "complex-construction",
    1310      complex_object_get_type,
    1311      test_construction_setup,
    1312      test_construction_init,
    1313      test_complex_construction_run,
    1314      test_construction_finish,
    1315      test_construction_teardown,
    1316      test_construction_print_result
    1317    },
    1318    {
    1319      "complex-construction1",
    1320      complex_object_get_type,
    1321      test_construction_setup,
    1322      test_construction_init,
    1323      test_complex_construction_run1,
    1324      test_construction_finish,
    1325      test_construction_teardown,
    1326      test_construction_print_result
    1327    },
    1328    {
    1329      "complex-construction2",
    1330      complex_object_get_type,
    1331      test_construction_setup,
    1332      test_construction_init,
    1333      test_complex_construction_run2,
    1334      test_construction_finish,
    1335      test_construction_teardown,
    1336      test_construction_print_result
    1337    },
    1338    {
    1339      "finalization",
    1340      simple_object_get_type,
    1341      test_construction_setup,
    1342      test_finalization_init,
    1343      test_finalization_run,
    1344      test_finalization_finish,
    1345      test_construction_teardown,
    1346      test_finalization_print_result
    1347    },
    1348    {
    1349      "type-check",
    1350      NULL,
    1351      test_type_check_setup,
    1352      test_type_check_init,
    1353      test_type_check_run,
    1354      test_type_check_finish,
    1355      test_type_check_teardown,
    1356      test_type_check_print_result
    1357    },
    1358    {
    1359      "emit-unhandled",
    1360      GINT_TO_POINTER (COMPLEX_SIGNAL),
    1361      test_emission_unhandled_setup,
    1362      test_emission_unhandled_init,
    1363      test_emission_run,
    1364      test_emission_unhandled_finish,
    1365      test_emission_unhandled_teardown,
    1366      test_emission_unhandled_print_result
    1367    },
    1368    {
    1369      "emit-unhandled-empty",
    1370      GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY),
    1371      test_emission_unhandled_setup,
    1372      test_emission_unhandled_init,
    1373      test_emission_run,
    1374      test_emission_unhandled_finish,
    1375      test_emission_unhandled_teardown,
    1376      test_emission_unhandled_print_result
    1377    },
    1378    {
    1379      "emit-unhandled-generic",
    1380      GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC),
    1381      test_emission_unhandled_setup,
    1382      test_emission_unhandled_init,
    1383      test_emission_run,
    1384      test_emission_unhandled_finish,
    1385      test_emission_unhandled_teardown,
    1386      test_emission_unhandled_print_result
    1387    },
    1388    {
    1389      "emit-unhandled-generic-empty",
    1390      GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY),
    1391      test_emission_unhandled_setup,
    1392      test_emission_unhandled_init,
    1393      test_emission_run,
    1394      test_emission_unhandled_finish,
    1395      test_emission_unhandled_teardown,
    1396      test_emission_unhandled_print_result
    1397    },
    1398    {
    1399      "emit-unhandled-args",
    1400      GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS),
    1401      test_emission_unhandled_setup,
    1402      test_emission_unhandled_init,
    1403      test_emission_run_args,
    1404      test_emission_unhandled_finish,
    1405      test_emission_unhandled_teardown,
    1406      test_emission_unhandled_print_result
    1407    },
    1408    {
    1409      "emit-handled",
    1410      GINT_TO_POINTER (COMPLEX_SIGNAL),
    1411      test_emission_handled_setup,
    1412      test_emission_handled_init,
    1413      test_emission_run,
    1414      test_emission_handled_finish,
    1415      test_emission_handled_teardown,
    1416      test_emission_handled_print_result
    1417    },
    1418    {
    1419      "emit-handled-empty",
    1420      GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY),
    1421      test_emission_handled_setup,
    1422      test_emission_handled_init,
    1423      test_emission_run,
    1424      test_emission_handled_finish,
    1425      test_emission_handled_teardown,
    1426      test_emission_handled_print_result
    1427    },
    1428    {
    1429      "emit-handled-generic",
    1430      GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC),
    1431      test_emission_handled_setup,
    1432      test_emission_handled_init,
    1433      test_emission_run,
    1434      test_emission_handled_finish,
    1435      test_emission_handled_teardown,
    1436      test_emission_handled_print_result
    1437    },
    1438    {
    1439      "emit-handled-generic-empty",
    1440      GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY),
    1441      test_emission_handled_setup,
    1442      test_emission_handled_init,
    1443      test_emission_run,
    1444      test_emission_handled_finish,
    1445      test_emission_handled_teardown,
    1446      test_emission_handled_print_result
    1447    },
    1448    {
    1449      "emit-handled-args",
    1450      GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS),
    1451      test_emission_handled_setup,
    1452      test_emission_handled_init,
    1453      test_emission_run_args,
    1454      test_emission_handled_finish,
    1455      test_emission_handled_teardown,
    1456      test_emission_handled_print_result
    1457    },
    1458    {
    1459      "notify-unhandled",
    1460      complex_object_get_type,
    1461      test_notify_unhandled_setup,
    1462      test_notify_unhandled_init,
    1463      test_notify_run,
    1464      test_notify_unhandled_finish,
    1465      test_notify_unhandled_teardown,
    1466      test_notify_unhandled_print_result
    1467    },
    1468    {
    1469      "notify-by-pspec-unhandled",
    1470      complex_object_get_type,
    1471      test_notify_unhandled_setup,
    1472      test_notify_unhandled_init,
    1473      test_notify_by_pspec_run,
    1474      test_notify_unhandled_finish,
    1475      test_notify_unhandled_teardown,
    1476      test_notify_unhandled_print_result
    1477    },
    1478    {
    1479      "notify-handled",
    1480      complex_object_get_type,
    1481      test_notify_handled_setup,
    1482      test_notify_handled_init,
    1483      test_notify_run,
    1484      test_notify_handled_finish,
    1485      test_notify_handled_teardown,
    1486      test_notify_handled_print_result
    1487    },
    1488    {
    1489      "notify-by-pspec-handled",
    1490      complex_object_get_type,
    1491      test_notify_handled_setup,
    1492      test_notify_handled_init,
    1493      test_notify_by_pspec_run,
    1494      test_notify_handled_finish,
    1495      test_notify_handled_teardown,
    1496      test_notify_handled_print_result
    1497    },
    1498    {
    1499      "property-set",
    1500      complex_object_get_type,
    1501      test_set_setup,
    1502      test_set_init,
    1503      test_set_run,
    1504      test_set_finish,
    1505      test_set_teardown,
    1506      test_set_print_result
    1507    },
    1508    {
    1509      "property-get",
    1510      complex_object_get_type,
    1511      test_get_setup,
    1512      test_get_init,
    1513      test_get_run,
    1514      test_get_finish,
    1515      test_get_teardown,
    1516      test_get_print_result
    1517    },
    1518    {
    1519      "refcount",
    1520      NULL,
    1521      test_refcount_setup,
    1522      test_refcount_init,
    1523      test_refcount_run,
    1524      test_refcount_finish,
    1525      test_refcount_teardown,
    1526      test_refcount_print_result
    1527    }
    1528  };
    1529  
    1530  static PerformanceTest *
    1531  find_test (const char *name)
    1532  {
    1533    gsize i;
    1534    for (i = 0; i < G_N_ELEMENTS (tests); i++)
    1535      {
    1536        if (strcmp (tests[i].name, name) == 0)
    1537  	return &tests[i];
    1538      }
    1539    return NULL;
    1540  }
    1541  int
    1542  main (int   argc,
    1543        char *argv[])
    1544  {
    1545    PerformanceTest *test;
    1546    GOptionContext *context;
    1547    GError *error = NULL;
    1548    int i;
    1549  
    1550    context = g_option_context_new ("GObject performance tests");
    1551    g_option_context_add_main_entries (context, cmd_entries, NULL);
    1552    if (!g_option_context_parse (context, &argc, &argv, &error))
    1553      {
    1554        g_printerr ("%s: %s\n", argv[0], error->message);
    1555        return 1;
    1556      }
    1557  
    1558    if (argc > 1)
    1559      {
    1560        for (i = 1; i < argc; i++)
    1561  	{
    1562  	  test = find_test (argv[i]);
    1563  	  if (test)
    1564  	    run_test (test);
    1565  	}
    1566      }
    1567    else
    1568      {
    1569        gsize k;
    1570        for (k = 0; k < G_N_ELEMENTS (tests); k++)
    1571          run_test (&tests[k]);
    1572      }
    1573  
    1574    g_option_context_free (context);
    1575    return 0;
    1576  }