Imported Upstream version 2.73.3
[platform/upstream/glib.git] / gobject / tests / performance / 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   char *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   N_PROPERTIES
286 };
287
288 static GParamSpec *pspecs[N_PROPERTIES] = { NULL, };
289
290 enum {
291   COMPLEX_SIGNAL,
292   COMPLEX_SIGNAL_EMPTY,
293   COMPLEX_SIGNAL_GENERIC,
294   COMPLEX_SIGNAL_GENERIC_EMPTY,
295   COMPLEX_SIGNAL_ARGS,
296   COMPLEX_LAST_SIGNAL
297 };
298
299 static guint complex_signals[COMPLEX_LAST_SIGNAL] = { 0 };
300
301 static void
302 complex_object_finalize (GObject *object)
303 {
304   ComplexObject *c = COMPLEX_OBJECT (object);
305
306   g_free (c->val2);
307
308   G_OBJECT_CLASS (complex_object_parent_class)->finalize (object);
309 }
310
311 static void
312 complex_object_set_property (GObject         *object,
313                              guint            prop_id,
314                              const GValue    *value,
315                              GParamSpec      *pspec)
316 {
317   ComplexObject *complex = COMPLEX_OBJECT (object);
318
319   switch (prop_id)
320     {
321     case PROP_VAL1:
322       complex->val1 = g_value_get_int (value);
323       break;
324     case PROP_VAL2:
325       g_free (complex->val2);
326       complex->val2 = g_value_dup_string (value);
327       break;
328     default:
329       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
330       break;
331     }
332 }
333
334 static void
335 complex_object_get_property (GObject         *object,
336                              guint            prop_id,
337                              GValue          *value,
338                              GParamSpec      *pspec)
339 {
340   ComplexObject *complex = COMPLEX_OBJECT (object);
341
342   switch (prop_id)
343     {
344     case PROP_VAL1:
345       g_value_set_int (value, complex->val1);
346       break;
347     case PROP_VAL2:
348       g_value_set_string (value, complex->val2);
349       break;
350     default:
351       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
352       break;
353     }
354 }
355
356 static void
357 complex_object_real_signal (ComplexObject *obj)
358 {
359 }
360
361 static void
362 complex_object_class_init (ComplexObjectClass *class)
363 {
364   GObjectClass *object_class = G_OBJECT_CLASS (class);
365
366   object_class->finalize = complex_object_finalize;
367   object_class->set_property = complex_object_set_property;
368   object_class->get_property = complex_object_get_property;
369
370   class->signal = complex_object_real_signal;
371
372   complex_signals[COMPLEX_SIGNAL] =
373     g_signal_new ("signal",
374                   G_TYPE_FROM_CLASS (object_class),
375                   G_SIGNAL_RUN_FIRST,
376                   G_STRUCT_OFFSET (ComplexObjectClass, signal),
377                   NULL, NULL,
378                   g_cclosure_marshal_VOID__VOID,
379                   G_TYPE_NONE, 0);
380
381   complex_signals[COMPLEX_SIGNAL_EMPTY] =
382     g_signal_new ("signal-empty",
383                   G_TYPE_FROM_CLASS (object_class),
384                   G_SIGNAL_RUN_FIRST,
385                   G_STRUCT_OFFSET (ComplexObjectClass, signal_empty),
386                   NULL, NULL,
387                   g_cclosure_marshal_VOID__VOID,
388                   G_TYPE_NONE, 0);
389
390   complex_signals[COMPLEX_SIGNAL_GENERIC] =
391     g_signal_new ("signal-generic",
392                   G_TYPE_FROM_CLASS (object_class),
393                   G_SIGNAL_RUN_FIRST,
394                   G_STRUCT_OFFSET (ComplexObjectClass, signal),
395                   NULL, NULL,
396                   NULL,
397                   G_TYPE_NONE, 0);
398   complex_signals[COMPLEX_SIGNAL_GENERIC_EMPTY] =
399     g_signal_new ("signal-generic-empty",
400                   G_TYPE_FROM_CLASS (object_class),
401                   G_SIGNAL_RUN_FIRST,
402                   G_STRUCT_OFFSET (ComplexObjectClass, signal_empty),
403                   NULL, NULL,
404                   NULL,
405                   G_TYPE_NONE, 0);
406
407   complex_signals[COMPLEX_SIGNAL_ARGS] =
408     g_signal_new ("signal-args",
409                   G_TYPE_FROM_CLASS (object_class),
410                   G_SIGNAL_RUN_FIRST,
411                   G_STRUCT_OFFSET (ComplexObjectClass, signal),
412                   NULL, NULL,
413                   g_cclosure_marshal_VOID__UINT_POINTER,
414                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
415
416   pspecs[PROP_VAL1] = g_param_spec_int ("val1", "val1", "val1",
417                                         0, G_MAXINT, 42,
418                                         G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
419   pspecs[PROP_VAL2] = g_param_spec_string ("val2", "val2", "val2",
420                                            NULL,
421                                            G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
422
423   g_object_class_install_properties (object_class, N_PROPERTIES, pspecs);
424 }
425
426 static void
427 complex_object_iface_method (TestIface *obj)
428 {
429   ComplexObject *complex = COMPLEX_OBJECT (obj);
430   complex->val1++;
431 }
432
433 static void
434 complex_test_iface_init (gpointer         g_iface,
435                          gpointer         iface_data)
436 {
437   TestIfaceClass *iface = g_iface;
438   iface->method = complex_object_iface_method;
439 }
440
441 static void
442 complex_object_init (ComplexObject *complex_object)
443 {
444   complex_object->val1 = 42;
445 }
446
447 /*************************************************************
448  * Test object construction performance
449  *************************************************************/
450
451 #define NUM_OBJECT_TO_CONSTRUCT 10000
452
453 struct ConstructionTest {
454   GObject **objects;
455   int n_objects;
456   GType type;
457 };
458
459 static gpointer
460 test_construction_setup (PerformanceTest *test)
461 {
462   struct ConstructionTest *data;
463
464   data = g_new0 (struct ConstructionTest, 1);
465   data->type = ((GType (*)(void))test->extra_data)();
466
467   return data;
468 }
469
470 static void
471 test_construction_init (PerformanceTest *test,
472                         gpointer _data,
473                         double count_factor)
474 {
475   struct ConstructionTest *data = _data;
476   int n;
477
478   n = NUM_OBJECT_TO_CONSTRUCT * count_factor;
479   if (data->n_objects != n)
480     {
481       data->n_objects = n;
482       data->objects = g_renew (GObject *, data->objects, n);
483     }
484 }
485
486 static void
487 test_construction_run (PerformanceTest *test,
488                        gpointer _data)
489 {
490   struct ConstructionTest *data = _data;
491   GObject **objects = data->objects;
492   GType type = data->type;
493   int i, n_objects;
494
495   n_objects = data->n_objects;
496   for (i = 0; i < n_objects; i++)
497     objects[i] = g_object_new (type, NULL);
498 }
499
500 static void
501 test_construction_run1 (PerformanceTest *test,
502                         gpointer _data)
503 {
504   struct ConstructionTest *data = _data;
505   GObject **objects = data->objects;
506   int i, n_objects;
507
508   n_objects = data->n_objects;
509   for (i = 0; i < n_objects; i++)
510     objects[i] = (GObject *) g_slice_new0 (SimpleObject);
511 }
512
513 static void
514 test_complex_construction_run (PerformanceTest *test,
515                                gpointer _data)
516 {
517   struct ConstructionTest *data = _data;
518   GObject **objects = data->objects;
519   GType type = data->type;
520   int i, n_objects;
521
522   n_objects = data->n_objects;
523   for (i = 0; i < n_objects; i++)
524     objects[i] = g_object_new (type, "val1", 5, "val2", "thousand", NULL);
525 }
526
527 static void
528 test_complex_construction_run1 (PerformanceTest *test,
529                                 gpointer _data)
530 {
531   struct ConstructionTest *data = _data;
532   GObject **objects = data->objects;
533   GType type = data->type;
534   int i, n_objects;
535
536   n_objects = data->n_objects;
537   for (i = 0; i < n_objects; i++)
538     {
539       ComplexObject *object;
540       object = (ComplexObject *)g_object_new (type, NULL);
541       object->val1 = 5;
542       object->val2 = g_strdup ("thousand");
543       objects[i] = (GObject *)object;
544     }
545 }
546
547 static void
548 test_complex_construction_run2 (PerformanceTest *test,
549                                 gpointer _data)
550 {
551   struct ConstructionTest *data = _data;
552   GObject **objects = data->objects;
553   GType type = data->type;
554   int i, n_objects;
555
556   n_objects = data->n_objects;
557   for (i = 0; i < n_objects; i++)
558     {
559       objects[i] = g_object_new (type, NULL);
560     }
561 }
562
563 static void
564 test_construction_finish (PerformanceTest *test,
565                           gpointer _data)
566 {
567   struct ConstructionTest *data = _data;
568   int i;
569
570   for (i = 0; i < data->n_objects; i++)
571     g_object_unref (data->objects[i]);
572 }
573
574 static void
575 test_construction_finish1 (PerformanceTest *test,
576                            gpointer _data)
577 {
578   struct ConstructionTest *data = _data;
579   int i;
580
581   for (i = 0; i < data->n_objects; i++)
582     g_slice_free (SimpleObject, (SimpleObject *)data->objects[i]);
583 }
584
585 static void
586 test_construction_teardown (PerformanceTest *test,
587                             gpointer _data)
588 {
589   struct ConstructionTest *data = _data;
590   g_free (data->objects);
591   g_free (data);
592 }
593
594 static void
595 test_finalization_init (PerformanceTest *test,
596                         gpointer _data,
597                         double count_factor)
598 {
599   struct ConstructionTest *data = _data;
600   int n;
601
602   n = NUM_OBJECT_TO_CONSTRUCT * count_factor;
603   if (data->n_objects != n)
604     {
605       data->n_objects = n;
606       data->objects = g_renew (GObject *, data->objects, n);
607     }
608
609   for (int i = 0; i <  data->n_objects; i++)
610     {
611       data->objects[i] = g_object_new (data->type, NULL);
612     }
613 }
614
615 static void
616 test_finalization_run (PerformanceTest *test,
617                        gpointer _data)
618 {
619   struct ConstructionTest *data = _data;
620   GObject **objects = data->objects;
621   int i, n_objects;
622
623   n_objects = data->n_objects;
624   for (i = 0; i < n_objects; i++)
625     {
626       g_object_unref (objects[i]);
627     }
628 }
629
630 static void
631 test_finalization_finish (PerformanceTest *test,
632                           gpointer _data)
633 {
634 }
635
636 static void
637 test_construction_print_result (PerformanceTest *test,
638                                 gpointer _data,
639                                 double time)
640 {
641   struct ConstructionTest *data = _data;
642
643   g_print ("Millions of constructed objects per second: %.3f\n",
644            data->n_objects / (time * 1000000));
645 }
646
647 static void
648 test_finalization_print_result (PerformanceTest *test,
649                                 gpointer _data,
650                                 double time)
651 {
652   struct ConstructionTest *data = _data;
653
654   g_print ("Millions of finalized objects per second: %.3f\n",
655            data->n_objects / (time * 1000000));
656 }
657
658 /*************************************************************
659  * Test runtime type check performance
660  *************************************************************/
661
662 #define NUM_KILO_CHECKS_PER_ROUND 50
663
664 struct TypeCheckTest {
665   GObject *object;
666   int n_checks;
667 };
668
669 static gpointer
670 test_type_check_setup (PerformanceTest *test)
671 {
672   struct TypeCheckTest *data;
673
674   data = g_new0 (struct TypeCheckTest, 1);
675   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
676
677   return data;
678 }
679
680 static void
681 test_type_check_init (PerformanceTest *test,
682                       gpointer _data,
683                       double factor)
684 {
685   struct TypeCheckTest *data = _data;
686
687   data->n_checks = factor * NUM_KILO_CHECKS_PER_ROUND;
688 }
689
690
691 /* Work around g_type_check_instance_is_a being marked "pure",
692    and thus only called once for the loop. */
693 gboolean (*my_type_check_instance_is_a) (GTypeInstance *type_instance,
694                                          GType          iface_type) = &g_type_check_instance_is_a;
695
696 static void
697 test_type_check_run (PerformanceTest *test,
698                      gpointer _data)
699 {
700   struct TypeCheckTest *data = _data;
701   GObject *object = data->object;
702   GType type, types[5];
703   int i, j;
704
705   types[0] = test_iface1_get_type ();
706   types[1] = test_iface2_get_type ();
707   types[2] = test_iface3_get_type ();
708   types[3] = test_iface4_get_type ();
709   types[4] = test_iface5_get_type ();
710
711   for (i = 0; i < data->n_checks; i++)
712     {
713       type = types[i%5];
714       for (j = 0; j < 1000; j++)
715         {
716           my_type_check_instance_is_a ((GTypeInstance *)object,
717                                        type);
718         }
719     }
720 }
721
722 static void
723 test_type_check_finish (PerformanceTest *test,
724                         gpointer data)
725 {
726 }
727
728 static void
729 test_type_check_print_result (PerformanceTest *test,
730                               gpointer _data,
731                               double time)
732 {
733   struct TypeCheckTest *data = _data;
734   g_print ("Million type checks per second: %.2f\n",
735            data->n_checks / (1000*time));
736 }
737
738 static void
739 test_type_check_teardown (PerformanceTest *test,
740                           gpointer _data)
741 {
742   struct TypeCheckTest *data = _data;
743
744   g_object_unref (data->object);
745   g_free (data);
746 }
747
748 /*************************************************************
749  * Test signal emissions performance (common code)
750  *************************************************************/
751
752 #define NUM_EMISSIONS_PER_ROUND 10000
753
754 struct EmissionTest {
755   GObject *object;
756   int n_checks;
757   int signal_id;
758 };
759
760 static void
761 test_emission_run (PerformanceTest *test,
762                              gpointer _data)
763 {
764   struct EmissionTest *data = _data;
765   GObject *object = data->object;
766   int i;
767
768   for (i = 0; i < data->n_checks; i++)
769     g_signal_emit (object, data->signal_id, 0);
770 }
771
772 static void
773 test_emission_run_args (PerformanceTest *test,
774                         gpointer _data)
775 {
776   struct EmissionTest *data = _data;
777   GObject *object = data->object;
778   int i;
779
780   for (i = 0; i < data->n_checks; i++)
781     g_signal_emit (object, data->signal_id, 0, 0, NULL);
782 }
783
784 /*************************************************************
785  * Test signal unhandled emissions performance
786  *************************************************************/
787
788 static gpointer
789 test_emission_unhandled_setup (PerformanceTest *test)
790 {
791   struct EmissionTest *data;
792
793   data = g_new0 (struct EmissionTest, 1);
794   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
795   data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)];
796   return data;
797 }
798
799 static void
800 test_emission_unhandled_init (PerformanceTest *test,
801                               gpointer _data,
802                               double factor)
803 {
804   struct EmissionTest *data = _data;
805
806   data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
807 }
808
809 static void
810 test_emission_unhandled_finish (PerformanceTest *test,
811                                 gpointer data)
812 {
813 }
814
815 static void
816 test_emission_unhandled_print_result (PerformanceTest *test,
817                                       gpointer _data,
818                                       double time)
819 {
820   struct EmissionTest *data = _data;
821
822   g_print ("Emissions per second: %.0f\n",
823            data->n_checks / time);
824 }
825
826 static void
827 test_emission_unhandled_teardown (PerformanceTest *test,
828                                   gpointer _data)
829 {
830   struct EmissionTest *data = _data;
831
832   g_object_unref (data->object);
833   g_free (data);
834 }
835
836 /*************************************************************
837  * Test signal handled emissions performance
838  *************************************************************/
839
840 static void
841 test_emission_handled_handler (ComplexObject *obj, gpointer data)
842 {
843 }
844
845 static gpointer
846 test_emission_handled_setup (PerformanceTest *test)
847 {
848   struct EmissionTest *data;
849
850   data = g_new0 (struct EmissionTest, 1);
851   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
852   data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)];
853   g_signal_connect (data->object, "signal",
854                     G_CALLBACK (test_emission_handled_handler),
855                     NULL);
856   g_signal_connect (data->object, "signal-empty",
857                     G_CALLBACK (test_emission_handled_handler),
858                     NULL);
859   g_signal_connect (data->object, "signal-generic",
860                     G_CALLBACK (test_emission_handled_handler),
861                     NULL);
862   g_signal_connect (data->object, "signal-generic-empty",
863                     G_CALLBACK (test_emission_handled_handler),
864                     NULL);
865   g_signal_connect (data->object, "signal-args",
866                     G_CALLBACK (test_emission_handled_handler),
867                     NULL);
868
869   return data;
870 }
871
872 static void
873 test_emission_handled_init (PerformanceTest *test,
874                             gpointer _data,
875                             double factor)
876 {
877   struct EmissionTest *data = _data;
878
879   data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
880 }
881
882 static void
883 test_emission_handled_finish (PerformanceTest *test,
884                               gpointer data)
885 {
886 }
887
888 static void
889 test_emission_handled_print_result (PerformanceTest *test,
890                                     gpointer _data,
891                                     double time)
892 {
893   struct EmissionTest *data = _data;
894
895   g_print ("Emissions per second: %.0f\n",
896            data->n_checks / time);
897 }
898
899 static void
900 test_emission_handled_teardown (PerformanceTest *test,
901                                 gpointer _data)
902 {
903   struct EmissionTest *data = _data;
904
905   g_object_unref (data->object);
906   g_free (data);
907 }
908
909 /*************************************************************
910  * Test object refcount performance
911  *************************************************************/
912
913 #define NUM_KILO_REFS_PER_ROUND 100000
914
915 struct RefcountTest {
916   GObject *object;
917   int n_checks;
918 };
919
920 static gpointer
921 test_refcount_setup (PerformanceTest *test)
922 {
923   struct RefcountTest *data;
924
925   data = g_new0 (struct RefcountTest, 1);
926   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
927
928   return data;
929 }
930
931 static void
932 test_refcount_init (PerformanceTest *test,
933                     gpointer _data,
934                     double factor)
935 {
936   struct RefcountTest *data = _data;
937
938   data->n_checks = factor * NUM_KILO_REFS_PER_ROUND;
939 }
940
941 static void
942 test_refcount_run (PerformanceTest *test,
943                    gpointer _data)
944 {
945   struct RefcountTest *data = _data;
946   GObject *object = data->object;
947   int i;
948
949   for (i = 0; i < data->n_checks; i++)
950     {
951       g_object_ref (object);
952       g_object_ref (object);
953       g_object_ref (object);
954       g_object_unref (object);
955       g_object_unref (object);
956
957       g_object_ref (object);
958       g_object_ref (object);
959       g_object_unref (object);
960       g_object_unref (object);
961       g_object_unref (object);
962     }
963 }
964
965 static void
966 test_refcount_finish (PerformanceTest *test,
967                       gpointer _data)
968 {
969 }
970
971 static void
972 test_refcount_print_result (PerformanceTest *test,
973                               gpointer _data,
974                               double time)
975 {
976   struct RefcountTest *data = _data;
977   g_print ("Million refs+unref per second: %.2f\n",
978            data->n_checks * 5 / (time * 1000000 ));
979 }
980
981 static void
982 test_refcount_teardown (PerformanceTest *test,
983                           gpointer _data)
984 {
985   struct RefcountTest *data = _data;
986
987   g_object_unref (data->object);
988   g_free (data);
989 }
990
991 /*************************************************************
992  * Main test code
993  *************************************************************/
994
995 static PerformanceTest tests[] = {
996   {
997     "simple-construction",
998     simple_object_get_type,
999     test_construction_setup,
1000     test_construction_init,
1001     test_construction_run,
1002     test_construction_finish,
1003     test_construction_teardown,
1004     test_construction_print_result
1005   },
1006   {
1007     "simple-construction1",
1008     simple_object_get_type,
1009     test_construction_setup,
1010     test_construction_init,
1011     test_construction_run1,
1012     test_construction_finish1,
1013     test_construction_teardown,
1014     test_construction_print_result
1015   },
1016   {
1017     "complex-construction",
1018     complex_object_get_type,
1019     test_construction_setup,
1020     test_construction_init,
1021     test_complex_construction_run,
1022     test_construction_finish,
1023     test_construction_teardown,
1024     test_construction_print_result
1025   },
1026   {
1027     "complex-construction1",
1028     complex_object_get_type,
1029     test_construction_setup,
1030     test_construction_init,
1031     test_complex_construction_run1,
1032     test_construction_finish,
1033     test_construction_teardown,
1034     test_construction_print_result
1035   },
1036   {
1037     "complex-construction2",
1038     complex_object_get_type,
1039     test_construction_setup,
1040     test_construction_init,
1041     test_complex_construction_run2,
1042     test_construction_finish,
1043     test_construction_teardown,
1044     test_construction_print_result
1045   },
1046   {
1047     "finalization",
1048     simple_object_get_type,
1049     test_construction_setup,
1050     test_finalization_init,
1051     test_finalization_run,
1052     test_finalization_finish,
1053     test_construction_teardown,
1054     test_finalization_print_result
1055   },
1056   {
1057     "type-check",
1058     NULL,
1059     test_type_check_setup,
1060     test_type_check_init,
1061     test_type_check_run,
1062     test_type_check_finish,
1063     test_type_check_teardown,
1064     test_type_check_print_result
1065   },
1066   {
1067     "emit-unhandled",
1068     GINT_TO_POINTER (COMPLEX_SIGNAL),
1069     test_emission_unhandled_setup,
1070     test_emission_unhandled_init,
1071     test_emission_run,
1072     test_emission_unhandled_finish,
1073     test_emission_unhandled_teardown,
1074     test_emission_unhandled_print_result
1075   },
1076   {
1077     "emit-unhandled-empty",
1078     GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY),
1079     test_emission_unhandled_setup,
1080     test_emission_unhandled_init,
1081     test_emission_run,
1082     test_emission_unhandled_finish,
1083     test_emission_unhandled_teardown,
1084     test_emission_unhandled_print_result
1085   },
1086   {
1087     "emit-unhandled-generic",
1088     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC),
1089     test_emission_unhandled_setup,
1090     test_emission_unhandled_init,
1091     test_emission_run,
1092     test_emission_unhandled_finish,
1093     test_emission_unhandled_teardown,
1094     test_emission_unhandled_print_result
1095   },
1096   {
1097     "emit-unhandled-generic-empty",
1098     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY),
1099     test_emission_unhandled_setup,
1100     test_emission_unhandled_init,
1101     test_emission_run,
1102     test_emission_unhandled_finish,
1103     test_emission_unhandled_teardown,
1104     test_emission_unhandled_print_result
1105   },
1106   {
1107     "emit-unhandled-args",
1108     GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS),
1109     test_emission_unhandled_setup,
1110     test_emission_unhandled_init,
1111     test_emission_run_args,
1112     test_emission_unhandled_finish,
1113     test_emission_unhandled_teardown,
1114     test_emission_unhandled_print_result
1115   },
1116   {
1117     "emit-handled",
1118     GINT_TO_POINTER (COMPLEX_SIGNAL),
1119     test_emission_handled_setup,
1120     test_emission_handled_init,
1121     test_emission_run,
1122     test_emission_handled_finish,
1123     test_emission_handled_teardown,
1124     test_emission_handled_print_result
1125   },
1126   {
1127     "emit-handled-empty",
1128     GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY),
1129     test_emission_handled_setup,
1130     test_emission_handled_init,
1131     test_emission_run,
1132     test_emission_handled_finish,
1133     test_emission_handled_teardown,
1134     test_emission_handled_print_result
1135   },
1136   {
1137     "emit-handled-generic",
1138     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC),
1139     test_emission_handled_setup,
1140     test_emission_handled_init,
1141     test_emission_run,
1142     test_emission_handled_finish,
1143     test_emission_handled_teardown,
1144     test_emission_handled_print_result
1145   },
1146   {
1147     "emit-handled-generic-empty",
1148     GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY),
1149     test_emission_handled_setup,
1150     test_emission_handled_init,
1151     test_emission_run,
1152     test_emission_handled_finish,
1153     test_emission_handled_teardown,
1154     test_emission_handled_print_result
1155   },
1156   {
1157     "emit-handled-args",
1158     GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS),
1159     test_emission_handled_setup,
1160     test_emission_handled_init,
1161     test_emission_run_args,
1162     test_emission_handled_finish,
1163     test_emission_handled_teardown,
1164     test_emission_handled_print_result
1165   },
1166   {
1167     "refcount",
1168     NULL,
1169     test_refcount_setup,
1170     test_refcount_init,
1171     test_refcount_run,
1172     test_refcount_finish,
1173     test_refcount_teardown,
1174     test_refcount_print_result
1175   }
1176 };
1177
1178 static PerformanceTest *
1179 find_test (const char *name)
1180 {
1181   gsize i;
1182   for (i = 0; i < G_N_ELEMENTS (tests); i++)
1183     {
1184       if (strcmp (tests[i].name, name) == 0)
1185         return &tests[i];
1186     }
1187   return NULL;
1188 }
1189 int
1190 main (int   argc,
1191       char *argv[])
1192 {
1193   PerformanceTest *test;
1194   GOptionContext *context;
1195   GError *error = NULL;
1196   int i;
1197
1198   context = g_option_context_new ("GObject performance tests");
1199   g_option_context_add_main_entries (context, cmd_entries, NULL);
1200   if (!g_option_context_parse (context, &argc, &argv, &error))
1201     {
1202       g_printerr ("%s: %s\n", argv[0], error->message);
1203       return 1;
1204     }
1205
1206   if (argc > 1)
1207     {
1208       for (i = 1; i < argc; i++)
1209         {
1210           test = find_test (argv[i]);
1211           if (test)
1212             run_test (test);
1213         }
1214     }
1215   else
1216     {
1217       gsize k;
1218       for (k = 0; k < G_N_ELEMENTS (tests); k++)
1219         run_test (&tests[k]);
1220     }
1221
1222   g_option_context_free (context);
1223   return 0;
1224 }