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