[kdbus] KDBUS_ITEM_PAYLOAD_OFF items are (once again) relative to msg header
[platform/upstream/glib.git] / tests / gobject / performance.c
1 /* GObject - GLib Type, Object, Parameter and Signal Library
2  * Copyright (C) 2009 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <math.h>
19 #include <string.h>
20 #include <glib-object.h>
21 #include "testcommon.h"
22
23 #define WARM_UP_N_RUNS 50
24 #define ESTIMATE_ROUND_TIME_N_RUNS 5
25 #define DEFAULT_TEST_TIME 15 /* seconds */
26  /* The time we want each round to take, in seconds, this should
27   * be large enough compared to the timer resolution, but small
28   * enought that the risk of any random slowness will miss the
29   * running window */
30 #define TARGET_ROUND_TIME 0.008
31
32 static gboolean verbose = FALSE;
33 static int test_length = DEFAULT_TEST_TIME;
34
35 static GOptionEntry cmd_entries[] = {
36   {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
37    "Print extra information", NULL},
38   {"seconds", 's', 0, G_OPTION_ARG_INT, &test_length,
39    "Time to run each test in seconds", NULL},
40   {NULL}
41 };
42
43 typedef struct _PerformanceTest PerformanceTest;
44 struct _PerformanceTest {
45   const char *name;
46   gpointer extra_data;
47
48   gpointer (*setup) (PerformanceTest *test);
49   void (*init) (PerformanceTest *test,
50                 gpointer data,
51                 double factor);
52   void (*run) (PerformanceTest *test,
53                gpointer data);
54   void (*finish) (PerformanceTest *test,
55                   gpointer data);
56   void (*teardown) (PerformanceTest *test,
57                     gpointer data);
58   void (*print_result) (PerformanceTest *test,
59                         gpointer data,
60                         double time);
61 };
62
63 static void
64 run_test (PerformanceTest *test)
65 {
66   gpointer data = NULL;
67   guint64 i, num_rounds;
68   double elapsed, min_elapsed, max_elapsed, avg_elapsed, factor;
69   GTimer *timer;
70
71   g_print ("Running test %s\n", test->name);
72
73   /* Set up test */
74   timer = g_timer_new ();
75   data = test->setup (test);
76
77   if (verbose)
78     g_print ("Warming up\n");
79
80   g_timer_start (timer);
81
82   /* Warm up the test by doing a few runs */
83   for (i = 0; i < WARM_UP_N_RUNS; i++)
84     {
85       test->init (test, data, 1.0);
86       test->run (test, data);
87       test->finish (test, data);
88     }
89
90   g_timer_stop (timer);
91   elapsed = g_timer_elapsed (timer, NULL);
92
93   if (verbose)
94     {
95       g_print ("Warm up time: %.2f secs\n", elapsed);
96       g_print ("Estimating round time\n");
97     }
98
99   /* Estimate time for one run by doing a few test rounds */
100   min_elapsed = 0;
101   for (i = 0; i < ESTIMATE_ROUND_TIME_N_RUNS; i++)
102     {
103       test->init (test, data, 1.0);
104       g_timer_start (timer);
105       test->run (test, data);
106       g_timer_stop (timer);
107       test->finish (test, data);
108
109       elapsed = g_timer_elapsed (timer, NULL);
110       if (i == 0)
111         min_elapsed = elapsed;
112       else
113         min_elapsed = MIN (min_elapsed, elapsed);
114     }
115
116   factor = TARGET_ROUND_TIME / min_elapsed;
117
118   if (verbose)
119     g_print ("Uncorrected round time: %.4f msecs, correction factor %.2f\n", 1000*min_elapsed, factor);
120
121   /* Calculate number of rounds needed */
122   num_rounds = (test_length / TARGET_ROUND_TIME) + 1;
123
124   if (verbose)
125     g_print ("Running %"G_GINT64_MODIFIER"d rounds\n", num_rounds);
126
127   /* Run the test */
128   for (i = 0; i < num_rounds; i++)
129     {
130       test->init (test, data, factor);
131       g_timer_start (timer);
132       test->run (test, data);
133       g_timer_stop (timer);
134       test->finish (test, data);
135       elapsed = g_timer_elapsed (timer, NULL);
136
137       if (i == 0)
138         max_elapsed = min_elapsed = avg_elapsed = elapsed;
139       else
140         {
141           min_elapsed = MIN (min_elapsed, elapsed);
142           max_elapsed = MAX (max_elapsed, elapsed);
143           avg_elapsed += elapsed;
144         }
145     }
146
147   avg_elapsed = avg_elapsed / num_rounds;
148
149   if (verbose)
150     {
151       g_print ("Minimum corrected round time: %.2f msecs\n", min_elapsed * 1000);
152       g_print ("Maximum corrected round time: %.2f msecs\n", max_elapsed * 1000);
153       g_print ("Average corrected round time: %.2f msecs\n", avg_elapsed * 1000);
154     }
155   /* Print the results */
156   test->print_result (test, data, min_elapsed);
157
158   /* Tear down */
159   test->teardown (test, data);
160   g_timer_destroy (timer);
161 }
162
163 /*************************************************************
164  * Simple object is a very simple small GObject subclass
165  * with no properties, no signals, implementing no interfaces
166  *************************************************************/
167
168 static GType simple_object_get_type (void);
169 #define SIMPLE_TYPE_OBJECT        (simple_object_get_type ())
170 typedef struct _SimpleObject      SimpleObject;
171 typedef struct _SimpleObjectClass   SimpleObjectClass;
172
173 struct _SimpleObject
174 {
175   GObject parent_instance;
176   int val;
177 };
178
179 struct _SimpleObjectClass
180 {
181   GObjectClass parent_class;
182 };
183
184 G_DEFINE_TYPE (SimpleObject, simple_object, G_TYPE_OBJECT);
185
186 static void
187 simple_object_finalize (GObject *object)
188 {
189   G_OBJECT_CLASS (simple_object_parent_class)->finalize (object);
190 }
191
192 static void
193 simple_object_class_init (SimpleObjectClass *class)
194 {
195   GObjectClass *object_class = G_OBJECT_CLASS (class);
196
197   object_class->finalize = simple_object_finalize;
198 }
199
200 static void
201 simple_object_init (SimpleObject *simple_object)
202 {
203   simple_object->val = 42;
204 }
205
206 typedef struct _TestIfaceClass TestIfaceClass;
207 typedef struct _TestIfaceClass TestIface1Class;
208 typedef struct _TestIfaceClass TestIface2Class;
209 typedef struct _TestIfaceClass TestIface3Class;
210 typedef struct _TestIfaceClass TestIface4Class;
211 typedef struct _TestIfaceClass TestIface5Class;
212 typedef struct _TestIface TestIface;
213
214 struct _TestIfaceClass
215 {
216   GTypeInterface base_iface;
217   void (*method) (TestIface *obj);
218 };
219
220 static GType test_iface1_get_type (void);
221 static GType test_iface2_get_type (void);
222 static GType test_iface3_get_type (void);
223 static GType test_iface4_get_type (void);
224 static GType test_iface5_get_type (void);
225
226 #define TEST_TYPE_IFACE1 (test_iface1_get_type ())
227 #define TEST_TYPE_IFACE2 (test_iface2_get_type ())
228 #define TEST_TYPE_IFACE3 (test_iface3_get_type ())
229 #define TEST_TYPE_IFACE4 (test_iface4_get_type ())
230 #define TEST_TYPE_IFACE5 (test_iface5_get_type ())
231
232 static DEFINE_IFACE (TestIface1, test_iface1,  NULL, NULL)
233 static DEFINE_IFACE (TestIface2, test_iface2,  NULL, NULL)
234 static DEFINE_IFACE (TestIface3, test_iface3,  NULL, NULL)
235 static DEFINE_IFACE (TestIface4, test_iface4,  NULL, NULL)
236 static DEFINE_IFACE (TestIface5, test_iface5,  NULL, NULL)
237
238 /*************************************************************
239  * Complex object is a GObject subclass with a properties,
240  * construct properties, signals and implementing an interface.
241  *************************************************************/
242
243 static GType complex_object_get_type (void);
244 #define COMPLEX_TYPE_OBJECT        (complex_object_get_type ())
245 typedef struct _ComplexObject      ComplexObject;
246 typedef struct _ComplexObjectClass ComplexObjectClass;
247
248 struct _ComplexObject
249 {
250   GObject parent_instance;
251   int val1;
252   int val2;
253 };
254
255 struct _ComplexObjectClass
256 {
257   GObjectClass parent_class;
258
259   void (*signal) (ComplexObject *obj);
260   void (*signal_empty) (ComplexObject *obj);
261 };
262
263 static void complex_test_iface_init (gpointer         g_iface,
264                                      gpointer         iface_data);
265
266 G_DEFINE_TYPE_EXTENDED (ComplexObject, complex_object,
267                         G_TYPE_OBJECT, 0,
268                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE1,
269                                                complex_test_iface_init)
270                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE2,
271                                                complex_test_iface_init)
272                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE3,
273                                                complex_test_iface_init)
274                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE4,
275                                                complex_test_iface_init)
276                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE5,
277                                                complex_test_iface_init)
278                         );
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 };
287
288 enum {
289   COMPLEX_SIGNAL,
290   COMPLEX_SIGNAL_EMPTY,
291   COMPLEX_SIGNAL_GENERIC,
292   COMPLEX_SIGNAL_GENERIC_EMPTY,
293   COMPLEX_SIGNAL_ARGS,
294   COMPLEX_LAST_SIGNAL
295 };
296
297 static guint complex_signals[COMPLEX_LAST_SIGNAL] = { 0 };
298
299 static void
300 complex_object_finalize (GObject *object)
301 {
302   G_OBJECT_CLASS (complex_object_parent_class)->finalize (object);
303 }
304
305 static void
306 complex_object_set_property (GObject         *object,
307                              guint            prop_id,
308                              const GValue    *value,
309                              GParamSpec      *pspec)
310 {
311   ComplexObject *complex = COMPLEX_OBJECT (object);
312
313   switch (prop_id)
314     {
315     case PROP_VAL1:
316       complex->val1 = g_value_get_int (value);
317       break;
318     case PROP_VAL2:
319       complex->val2 = g_value_get_int (value);
320       break;
321     default:
322       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
323       break;
324     }
325 }
326
327 static void
328 complex_object_get_property (GObject         *object,
329                              guint            prop_id,
330                              GValue          *value,
331                              GParamSpec      *pspec)
332 {
333   ComplexObject *complex = COMPLEX_OBJECT (object);
334
335   switch (prop_id)
336     {
337     case PROP_VAL1:
338       g_value_set_int (value, complex->val1);
339       break;
340     case PROP_VAL2:
341       g_value_set_int (value, complex->val2);
342       break;
343     default:
344       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
345       break;
346     }
347 }
348
349 static void
350 complex_object_real_signal (ComplexObject *obj)
351 {
352 }
353
354 static void
355 complex_object_class_init (ComplexObjectClass *class)
356 {
357   GObjectClass *object_class = G_OBJECT_CLASS (class);
358
359   object_class->finalize = complex_object_finalize;
360   object_class->set_property = complex_object_set_property;
361   object_class->get_property = complex_object_get_property;
362
363   class->signal = complex_object_real_signal;
364
365   complex_signals[COMPLEX_SIGNAL] =
366     g_signal_new ("signal",
367                   G_TYPE_FROM_CLASS (object_class),
368                   G_SIGNAL_RUN_FIRST,
369                   G_STRUCT_OFFSET (ComplexObjectClass, signal),
370                   NULL, NULL,
371                   g_cclosure_marshal_VOID__VOID,
372                   G_TYPE_NONE, 0);
373
374   complex_signals[COMPLEX_SIGNAL_EMPTY] =
375     g_signal_new ("signal-empty",
376                   G_TYPE_FROM_CLASS (object_class),
377                   G_SIGNAL_RUN_FIRST,
378                   G_STRUCT_OFFSET (ComplexObjectClass, signal_empty),
379                   NULL, NULL,
380                   g_cclosure_marshal_VOID__VOID,
381                   G_TYPE_NONE, 0);
382
383   complex_signals[COMPLEX_SIGNAL_GENERIC] =
384     g_signal_new ("signal-generic",
385                   G_TYPE_FROM_CLASS (object_class),
386                   G_SIGNAL_RUN_FIRST,
387                   G_STRUCT_OFFSET (ComplexObjectClass, signal),
388                   NULL, NULL,
389                   NULL,
390                   G_TYPE_NONE, 0);
391   complex_signals[COMPLEX_SIGNAL_GENERIC_EMPTY] =
392     g_signal_new ("signal-generic-empty",
393                   G_TYPE_FROM_CLASS (object_class),
394                   G_SIGNAL_RUN_FIRST,
395                   G_STRUCT_OFFSET (ComplexObjectClass, signal_empty),
396                   NULL, NULL,
397                   NULL,
398                   G_TYPE_NONE, 0);
399
400   complex_signals[COMPLEX_SIGNAL_ARGS] =
401     g_signal_new ("signal-args",
402                   G_TYPE_FROM_CLASS (object_class),
403                   G_SIGNAL_RUN_FIRST,
404                   G_STRUCT_OFFSET (ComplexObjectClass, signal),
405                   NULL, NULL,
406                   g_cclosure_marshal_VOID__UINT_POINTER,
407                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
408
409   g_object_class_install_property (object_class,
410                                    PROP_VAL1,
411                                    g_param_spec_int ("val1",
412                                                      "val1",
413                                                      "val1",
414                                                      0,
415                                                      G_MAXINT,
416                                                      42,
417                                                      G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
418   g_object_class_install_property (object_class,
419                                    PROP_VAL2,
420                                    g_param_spec_int ("val2",
421                                                      "val2",
422                                                      "val2",
423                                                      0,
424                                                      G_MAXINT,
425                                                      43,
426                                                      G_PARAM_READWRITE));
427
428
429 }
430
431 static void
432 complex_object_iface_method (TestIface *obj)
433 {
434   ComplexObject *complex = COMPLEX_OBJECT (obj);
435   complex->val1++;
436 }
437
438 static void
439 complex_test_iface_init (gpointer         g_iface,
440                          gpointer         iface_data)
441 {
442   TestIfaceClass *iface = g_iface;
443   iface->method = complex_object_iface_method;
444 }
445
446 static void
447 complex_object_init (ComplexObject *complex_object)
448 {
449   complex_object->val2 = 43;
450 }
451
452 /*************************************************************
453  * Test object construction performance
454  *************************************************************/
455
456 #define NUM_OBJECT_TO_CONSTRUCT 10000
457
458 struct ConstructionTest {
459   GObject **objects;
460   int n_objects;
461   GType type;
462 };
463
464 static gpointer
465 test_construction_setup (PerformanceTest *test)
466 {
467   struct ConstructionTest *data;
468
469   data = g_new0 (struct ConstructionTest, 1);
470   data->type = ((GType (*)(void))test->extra_data)();
471
472   return data;
473 }
474
475 static void
476 test_construction_init (PerformanceTest *test,
477                         gpointer _data,
478                         double count_factor)
479 {
480   struct ConstructionTest *data = _data;
481   int n;
482
483   n = NUM_OBJECT_TO_CONSTRUCT * count_factor;
484   if (data->n_objects != n)
485     {
486       data->n_objects = n;
487       data->objects = g_new (GObject *, n);
488     }
489 }
490
491 static void
492 test_construction_run (PerformanceTest *test,
493                        gpointer _data)
494 {
495   struct ConstructionTest *data = _data;
496   GObject **objects = data->objects;
497   GType type = data->type;
498   int i, n_objects;
499
500   n_objects = data->n_objects;
501   for (i = 0; i < n_objects; i++)
502     objects[i] = g_object_new (type, NULL);
503 }
504
505 static void
506 test_construction_finish (PerformanceTest *test,
507                           gpointer _data)
508 {
509   struct ConstructionTest *data = _data;
510   int i;
511
512   for (i = 0; i < data->n_objects; i++)
513     g_object_unref (data->objects[i]);
514 }
515
516 static void
517 test_construction_teardown (PerformanceTest *test,
518                             gpointer _data)
519 {
520   struct ConstructionTest *data = _data;
521   g_free (data->objects);
522   g_free (data);
523 }
524
525 static void
526 test_construction_print_result (PerformanceTest *test,
527                                 gpointer _data,
528                                 double time)
529 {
530   struct ConstructionTest *data = _data;
531
532   g_print ("Millions of constructed objects per second: %.3f\n",
533            data->n_objects / (time * 1000000));
534 }
535
536 /*************************************************************
537  * Test runtime type check performance
538  *************************************************************/
539
540 #define NUM_KILO_CHECKS_PER_ROUND 50
541
542 struct TypeCheckTest {
543   GObject *object;
544   int n_checks;
545 };
546
547 static gpointer
548 test_type_check_setup (PerformanceTest *test)
549 {
550   struct TypeCheckTest *data;
551
552   data = g_new0 (struct TypeCheckTest, 1);
553   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
554
555   return data;
556 }
557
558 static void
559 test_type_check_init (PerformanceTest *test,
560                       gpointer _data,
561                       double factor)
562 {
563   struct TypeCheckTest *data = _data;
564
565   data->n_checks = factor * NUM_KILO_CHECKS_PER_ROUND;
566 }
567
568
569 /* Work around g_type_check_instance_is_a being marked "pure",
570    and thus only called once for the loop. */
571 gboolean (*my_type_check_instance_is_a) (GTypeInstance *type_instance,
572                                          GType          iface_type) = &g_type_check_instance_is_a;
573
574 static void
575 test_type_check_run (PerformanceTest *test,
576                      gpointer _data)
577 {
578   struct TypeCheckTest *data = _data;
579   volatile GObject *object = data->object;
580   volatile GType type, types[5];
581   int i, j;
582
583   types[0] = test_iface1_get_type ();
584   types[1] = test_iface2_get_type ();
585   types[2] = test_iface3_get_type ();
586   types[3] = test_iface4_get_type ();
587   types[4] = test_iface5_get_type ();
588
589   for (i = 0; i < data->n_checks; i++)
590     {
591       type = types[i%5];
592       for (j = 0; j < 1000; j++)
593         {
594           my_type_check_instance_is_a ((GTypeInstance *)object,
595                                        type);
596         }
597     }
598 }
599
600 static void
601 test_type_check_finish (PerformanceTest *test,
602                         gpointer data)
603 {
604 }
605
606 static void
607 test_type_check_print_result (PerformanceTest *test,
608                               gpointer _data,
609                               double time)
610 {
611   struct TypeCheckTest *data = _data;
612   g_print ("Million type checks per second: %.2f\n",
613            data->n_checks / (1000*time));
614 }
615
616 static void
617 test_type_check_teardown (PerformanceTest *test,
618                           gpointer _data)
619 {
620   struct TypeCheckTest *data = _data;
621
622   g_object_unref (data->object);
623   g_free (data);
624 }
625
626 /*************************************************************
627  * Test signal emissions performance (common code)
628  *************************************************************/
629
630 #define NUM_EMISSIONS_PER_ROUND 10000
631
632 struct EmissionTest {
633   GObject *object;
634   int n_checks;
635   int signal_id;
636 };
637
638 static void
639 test_emission_run (PerformanceTest *test,
640                              gpointer _data)
641 {
642   struct EmissionTest *data = _data;
643   GObject *object = data->object;
644   int i;
645
646   for (i = 0; i < data->n_checks; i++)
647     g_signal_emit (object, data->signal_id, 0);
648 }
649
650 static void
651 test_emission_run_args (PerformanceTest *test,
652                         gpointer _data)
653 {
654   struct EmissionTest *data = _data;
655   GObject *object = data->object;
656   int i;
657
658   for (i = 0; i < data->n_checks; i++)
659     g_signal_emit (object, data->signal_id, 0, 0, NULL);
660 }
661
662 /*************************************************************
663  * Test signal unhandled emissions performance
664  *************************************************************/
665
666 static gpointer
667 test_emission_unhandled_setup (PerformanceTest *test)
668 {
669   struct EmissionTest *data;
670
671   data = g_new0 (struct EmissionTest, 1);
672   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
673   data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)];
674   return data;
675 }
676
677 static void
678 test_emission_unhandled_init (PerformanceTest *test,
679                               gpointer _data,
680                               double factor)
681 {
682   struct EmissionTest *data = _data;
683
684   data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
685 }
686
687 static void
688 test_emission_unhandled_finish (PerformanceTest *test,
689                                 gpointer data)
690 {
691 }
692
693 static void
694 test_emission_unhandled_print_result (PerformanceTest *test,
695                                       gpointer _data,
696                                       double time)
697 {
698   struct EmissionTest *data = _data;
699
700   g_print ("Emissions per second: %.0f\n",
701            data->n_checks / time);
702 }
703
704 static void
705 test_emission_unhandled_teardown (PerformanceTest *test,
706                                   gpointer _data)
707 {
708   struct EmissionTest *data = _data;
709
710   g_object_unref (data->object);
711   g_free (data);
712 }
713
714 /*************************************************************
715  * Test signal handled emissions performance
716  *************************************************************/
717
718 static void
719 test_emission_handled_handler (ComplexObject *obj, gpointer data)
720 {
721 }
722
723 static gpointer
724 test_emission_handled_setup (PerformanceTest *test)
725 {
726   struct EmissionTest *data;
727
728   data = g_new0 (struct EmissionTest, 1);
729   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
730   data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)];
731   g_signal_connect (data->object, "signal",
732                     G_CALLBACK (test_emission_handled_handler),
733                     NULL);
734   g_signal_connect (data->object, "signal-empty",
735                     G_CALLBACK (test_emission_handled_handler),
736                     NULL);
737   g_signal_connect (data->object, "signal-generic",
738                     G_CALLBACK (test_emission_handled_handler),
739                     NULL);
740   g_signal_connect (data->object, "signal-generic-empty",
741                     G_CALLBACK (test_emission_handled_handler),
742                     NULL);
743   g_signal_connect (data->object, "signal-args",
744                     G_CALLBACK (test_emission_handled_handler),
745                     NULL);
746
747   return data;
748 }
749
750 static void
751 test_emission_handled_init (PerformanceTest *test,
752                             gpointer _data,
753                             double factor)
754 {
755   struct EmissionTest *data = _data;
756
757   data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
758 }
759
760 static void
761 test_emission_handled_finish (PerformanceTest *test,
762                               gpointer data)
763 {
764 }
765
766 static void
767 test_emission_handled_print_result (PerformanceTest *test,
768                                     gpointer _data,
769                                     double time)
770 {
771   struct EmissionTest *data = _data;
772
773   g_print ("Emissions per second: %.0f\n",
774            data->n_checks / time);
775 }
776
777 static void
778 test_emission_handled_teardown (PerformanceTest *test,
779                                 gpointer _data)
780 {
781   struct EmissionTest *data = _data;
782
783   g_object_unref (data->object);
784   g_free (data);
785 }
786
787 /*************************************************************
788  * Test object refcount performance
789  *************************************************************/
790
791 #define NUM_KILO_REFS_PER_ROUND 100000
792
793 struct RefcountTest {
794   GObject *object;
795   int n_checks;
796 };
797
798 static gpointer
799 test_refcount_setup (PerformanceTest *test)
800 {
801   struct RefcountTest *data;
802
803   data = g_new0 (struct RefcountTest, 1);
804   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
805
806   return data;
807 }
808
809 static void
810 test_refcount_init (PerformanceTest *test,
811                     gpointer _data,
812                     double factor)
813 {
814   struct RefcountTest *data = _data;
815
816   data->n_checks = factor * NUM_KILO_REFS_PER_ROUND;
817 }
818
819 static void
820 test_refcount_run (PerformanceTest *test,
821                    gpointer _data)
822 {
823   struct RefcountTest *data = _data;
824   GObject *object = data->object;
825   int i;
826
827   for (i = 0; i < data->n_checks; i++)
828     {
829       g_object_ref (object);
830       g_object_ref (object);
831       g_object_ref (object);
832       g_object_unref (object);
833       g_object_unref (object);
834
835       g_object_ref (object);
836       g_object_ref (object);
837       g_object_unref (object);
838       g_object_unref (object);
839       g_object_unref (object);
840     }
841 }
842
843 static void
844 test_refcount_finish (PerformanceTest *test,
845                       gpointer _data)
846 {
847 }
848
849 static void
850 test_refcount_print_result (PerformanceTest *test,
851                               gpointer _data,
852                               double time)
853 {
854   struct RefcountTest *data = _data;
855   g_print ("Million refs+unref per second: %.2f\n",
856            data->n_checks * 5 / (time * 1000000 ));
857 }
858
859 static void
860 test_refcount_teardown (PerformanceTest *test,
861                           gpointer _data)
862 {
863   struct RefcountTest *data = _data;
864
865   g_object_unref (data->object);
866   g_free (data);
867 }
868
869 /*************************************************************
870  * Main test code
871  *************************************************************/
872
873 static PerformanceTest tests[] = {
874   {
875     "simple-construction",
876     simple_object_get_type,
877     test_construction_setup,
878     test_construction_init,
879     test_construction_run,
880     test_construction_finish,
881     test_construction_teardown,
882     test_construction_print_result
883   },
884   {
885     "complex-construction",
886     complex_object_get_type,
887     test_construction_setup,
888     test_construction_init,
889     test_construction_run,
890     test_construction_finish,
891     test_construction_teardown,
892     test_construction_print_result
893   },
894   {
895     "type-check",
896     NULL,
897     test_type_check_setup,
898     test_type_check_init,
899     test_type_check_run,
900     test_type_check_finish,
901     test_type_check_teardown,
902     test_type_check_print_result
903   },
904   {
905     "emit-unhandled",
906     GINT_TO_POINTER (COMPLEX_SIGNAL),
907     test_emission_unhandled_setup,
908     test_emission_unhandled_init,
909     test_emission_run,
910     test_emission_unhandled_finish,
911     test_emission_unhandled_teardown,
912     test_emission_unhandled_print_result
913   },
914   {
915     "emit-unhandled-empty",
916     GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY),
917     test_emission_unhandled_setup,
918     test_emission_unhandled_init,
919     test_emission_run,
920     test_emission_unhandled_finish,
921     test_emission_unhandled_teardown,
922     test_emission_unhandled_print_result
923   },
924   {
925     "emit-unhandled-generic",
926     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC),
927     test_emission_unhandled_setup,
928     test_emission_unhandled_init,
929     test_emission_run,
930     test_emission_unhandled_finish,
931     test_emission_unhandled_teardown,
932     test_emission_unhandled_print_result
933   },
934   {
935     "emit-unhandled-generic-empty",
936     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY),
937     test_emission_unhandled_setup,
938     test_emission_unhandled_init,
939     test_emission_run,
940     test_emission_unhandled_finish,
941     test_emission_unhandled_teardown,
942     test_emission_unhandled_print_result
943   },
944   {
945     "emit-unhandled-args",
946     GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS),
947     test_emission_unhandled_setup,
948     test_emission_unhandled_init,
949     test_emission_run_args,
950     test_emission_unhandled_finish,
951     test_emission_unhandled_teardown,
952     test_emission_unhandled_print_result
953   },
954   {
955     "emit-handled",
956     GINT_TO_POINTER (COMPLEX_SIGNAL),
957     test_emission_handled_setup,
958     test_emission_handled_init,
959     test_emission_run,
960     test_emission_handled_finish,
961     test_emission_handled_teardown,
962     test_emission_handled_print_result
963   },
964   {
965     "emit-handled-empty",
966     GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY),
967     test_emission_handled_setup,
968     test_emission_handled_init,
969     test_emission_run,
970     test_emission_handled_finish,
971     test_emission_handled_teardown,
972     test_emission_handled_print_result
973   },
974   {
975     "emit-handled-generic",
976     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC),
977     test_emission_handled_setup,
978     test_emission_handled_init,
979     test_emission_run,
980     test_emission_handled_finish,
981     test_emission_handled_teardown,
982     test_emission_handled_print_result
983   },
984   {
985     "emit-handled-generic-empty",
986     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY),
987     test_emission_handled_setup,
988     test_emission_handled_init,
989     test_emission_run,
990     test_emission_handled_finish,
991     test_emission_handled_teardown,
992     test_emission_handled_print_result
993   },
994   {
995     "emit-handled-args",
996     GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS),
997     test_emission_handled_setup,
998     test_emission_handled_init,
999     test_emission_run_args,
1000     test_emission_handled_finish,
1001     test_emission_handled_teardown,
1002     test_emission_handled_print_result
1003   },
1004   {
1005     "refcount",
1006     NULL,
1007     test_refcount_setup,
1008     test_refcount_init,
1009     test_refcount_run,
1010     test_refcount_finish,
1011     test_refcount_teardown,
1012     test_refcount_print_result
1013   }
1014 };
1015
1016 static PerformanceTest *
1017 find_test (const char *name)
1018 {
1019   int i;
1020   for (i = 0; i < G_N_ELEMENTS (tests); i++)
1021     {
1022       if (strcmp (tests[i].name, name) == 0)
1023         return &tests[i];
1024     }
1025   return NULL;
1026 }
1027 int
1028 main (int   argc,
1029       char *argv[])
1030 {
1031   PerformanceTest *test;
1032   GOptionContext *context;
1033   GError *error = NULL;
1034   int i;
1035
1036   context = g_option_context_new ("GObject performance tests");
1037   g_option_context_add_main_entries (context, cmd_entries, NULL);
1038   if (!g_option_context_parse (context, &argc, &argv, &error))
1039     {
1040       g_printerr ("%s: %s\n", argv[0], error->message);
1041       return 1;
1042     }
1043
1044   if (argc > 1)
1045     {
1046       for (i = 1; i < argc; i++)
1047         {
1048           test = find_test (argv[i]);
1049           if (test)
1050             run_test (test);
1051         }
1052     }
1053   else
1054     {
1055       for (i = 0; i < G_N_ELEMENTS (tests); i++)
1056         run_test (&tests[i]);
1057     }
1058
1059   return 0;
1060 }