tests: performance: add emit-handled/emit-unhandled tests
[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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <math.h>
21 #include <string.h>
22 #include <glib-object.h>
23 #include "testcommon.h"
24
25 #define WARM_UP_N_RUNS 50
26 #define ESTIMATE_ROUND_TIME_N_RUNS 5
27 #define DEFAULT_TEST_TIME 15 /* seconds */
28  /* The time we want each round to take, in seconds, this should
29   * be large enough compared to the timer resolution, but small
30   * enought that the risk of any random slowness will miss the
31   * running window */
32 #define TARGET_ROUND_TIME 0.004
33
34 static gboolean verbose = FALSE;
35 static int test_length = DEFAULT_TEST_TIME;
36
37 static GOptionEntry cmd_entries[] = {
38   {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
39    "Print extra information", NULL},
40   {"seconds", 's', 0, G_OPTION_ARG_INT, &test_length,
41    "Time to run each test in seconds", NULL},
42   {NULL}
43 };
44
45 typedef struct _PerformanceTest PerformanceTest;
46 struct _PerformanceTest {
47   const char *name;
48   gpointer extra_data;
49
50   gpointer (*setup) (PerformanceTest *test);
51   void (*init) (PerformanceTest *test,
52                 gpointer data,
53                 double factor);
54   void (*run) (PerformanceTest *test,
55                gpointer data);
56   void (*finish) (PerformanceTest *test,
57                   gpointer data);
58   void (*teardown) (PerformanceTest *test,
59                     gpointer data);
60   void (*print_result) (PerformanceTest *test,
61                         gpointer data,
62                         double time);
63 };
64
65 static void
66 run_test (PerformanceTest *test)
67 {
68   gpointer data = NULL;
69   guint64 i, num_rounds;
70   double elapsed, min_elapsed, factor;
71   GTimer *timer;
72
73   g_print ("Running test %s\n", test->name);
74
75   /* Set up test */
76   timer = g_timer_new ();
77   data = test->setup (test);
78
79   if (verbose)
80     g_print ("Warming up\n");
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   if (verbose)
91     g_print ("Estimating round time\n");
92
93   /* Estimate time for one run by doing a few test rounds */
94   min_elapsed = 0;
95   for (i = 0; i < ESTIMATE_ROUND_TIME_N_RUNS; i++)
96     {
97       test->init (test, data, 1.0);
98       g_timer_start (timer);
99       test->run (test, data);
100       g_timer_stop (timer);
101       test->finish (test, data);
102
103       elapsed = g_timer_elapsed (timer, NULL);
104       if (i == 0)
105         min_elapsed = elapsed;
106       else
107         min_elapsed = MIN (min_elapsed, elapsed);
108     }
109
110   factor = TARGET_ROUND_TIME / min_elapsed;
111
112   if (verbose)
113     g_print ("Uncorrected round time: %f.4 secs, correction factor %f.2\n", min_elapsed, factor);
114
115   /* Calculate number of rounds needed */
116   num_rounds = (test_length / TARGET_ROUND_TIME) + 1;
117
118   if (verbose)
119     g_print ("Running %"G_GINT64_MODIFIER"d rounds\n", num_rounds);
120
121   /* Run the test */
122   for (i = 0; i < num_rounds; i++)
123     {
124       test->init (test, data, factor);
125       g_timer_start (timer);
126       test->run (test, data);
127       g_timer_stop (timer);
128       test->finish (test, data);
129       elapsed = g_timer_elapsed (timer, NULL);
130
131       if (i == 0)
132         min_elapsed = elapsed;
133       else
134         min_elapsed = MIN (min_elapsed, elapsed);
135     }
136
137   if (verbose)
138     g_print ("Minimum corrected round time: %f secs\n", min_elapsed);
139
140   /* Print the results */
141   test->print_result (test, data, min_elapsed);
142
143   /* Tear down */
144   test->teardown (test, data);
145   g_timer_destroy (timer);
146 }
147
148 /*************************************************************
149  * Simple object is a very simple small GObject subclass
150  * with no properties, no signals, implementing no interfaces
151  *************************************************************/
152
153 #define SIMPLE_TYPE_OBJECT        (simple_object_get_type ())
154 typedef struct _SimpleObject      SimpleObject;
155 typedef struct _SimpleObjectClass   SimpleObjectClass;
156
157 struct _SimpleObject
158 {
159   GObject parent_instance;
160   int val;
161 };
162
163 struct _SimpleObjectClass
164 {
165   GObjectClass parent_class;
166 };
167
168 G_DEFINE_TYPE (SimpleObject, simple_object, G_TYPE_OBJECT);
169
170 static void
171 simple_object_finalize (GObject *object)
172 {
173   G_OBJECT_CLASS (simple_object_parent_class)->finalize (object);
174 }
175
176 static void
177 simple_object_class_init (SimpleObjectClass *class)
178 {
179   GObjectClass *object_class = G_OBJECT_CLASS (class);
180
181   object_class->finalize = simple_object_finalize;
182 }
183
184 static void
185 simple_object_init (SimpleObject *simple_object)
186 {
187   simple_object->val = 42;
188 }
189
190 typedef struct _TestIfaceClass TestIfaceClass;
191 typedef struct _TestIfaceClass TestIface1Class;
192 typedef struct _TestIfaceClass TestIface2Class;
193 typedef struct _TestIfaceClass TestIface3Class;
194 typedef struct _TestIfaceClass TestIface4Class;
195 typedef struct _TestIfaceClass TestIface5Class;
196 typedef struct _TestIface TestIface;
197
198 struct _TestIfaceClass
199 {
200   GTypeInterface base_iface;
201   void (*method) (TestIface *obj);
202 };
203
204 #define TEST_TYPE_IFACE1 (test_iface1_get_type ())
205 #define TEST_TYPE_IFACE2 (test_iface2_get_type ())
206 #define TEST_TYPE_IFACE3 (test_iface3_get_type ())
207 #define TEST_TYPE_IFACE4 (test_iface4_get_type ())
208 #define TEST_TYPE_IFACE5 (test_iface5_get_type ())
209
210 static DEFINE_IFACE (TestIface1, test_iface1,  NULL, NULL)
211 static DEFINE_IFACE (TestIface2, test_iface2,  NULL, NULL)
212 static DEFINE_IFACE (TestIface3, test_iface3,  NULL, NULL)
213 static DEFINE_IFACE (TestIface4, test_iface4,  NULL, NULL)
214 static DEFINE_IFACE (TestIface5, test_iface5,  NULL, NULL)
215
216 /*************************************************************
217  * Complex object is a GObject subclass with a properties,
218  * construct properties, signals and implementing an interface.
219  *************************************************************/
220
221 #define COMPLEX_TYPE_OBJECT        (complex_object_get_type ())
222 typedef struct _ComplexObject      ComplexObject;
223 typedef struct _ComplexObjectClass ComplexObjectClass;
224
225 struct _ComplexObject
226 {
227   GObject parent_instance;
228   int val1;
229   int val2;
230 };
231
232 struct _ComplexObjectClass
233 {
234   GObjectClass parent_class;
235
236   void (*signal) (ComplexObject *obj);
237 };
238
239 static void complex_test_iface_init (gpointer         g_iface,
240                                      gpointer         iface_data);
241
242 G_DEFINE_TYPE_EXTENDED (ComplexObject, complex_object,
243                         G_TYPE_OBJECT, 0,
244                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE1,
245                                                complex_test_iface_init)
246                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE2,
247                                                complex_test_iface_init)
248                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE3,
249                                                complex_test_iface_init)
250                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE4,
251                                                complex_test_iface_init)
252                         G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE5,
253                                                complex_test_iface_init)
254                         );
255
256 #define COMPLEX_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), COMPLEX_TYPE_OBJECT, ComplexObject))
257
258 enum {
259   PROP_0,
260   PROP_VAL1,
261   PROP_VAL2
262 };
263
264 enum {
265   COMPLEX_SIGNAL,
266   COMPLEX_LAST_SIGNAL
267 };
268
269 static guint complex_signals[COMPLEX_LAST_SIGNAL] = { 0 };
270
271 static void
272 complex_object_finalize (GObject *object)
273 {
274   G_OBJECT_CLASS (complex_object_parent_class)->finalize (object);
275 }
276
277 static void
278 complex_object_set_property (GObject         *object,
279                              guint            prop_id,
280                              const GValue    *value,
281                              GParamSpec      *pspec)
282 {
283   ComplexObject *complex = COMPLEX_OBJECT (object);
284
285   switch (prop_id)
286     {
287     case PROP_VAL1:
288       complex->val1 = g_value_get_int (value);
289       break;
290     case PROP_VAL2:
291       complex->val2 = g_value_get_int (value);
292       break;
293     default:
294       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
295       break;
296     }
297 }
298
299 static void
300 complex_object_get_property (GObject         *object,
301                              guint            prop_id,
302                              GValue          *value,
303                              GParamSpec      *pspec)
304 {
305   ComplexObject *complex = COMPLEX_OBJECT (object);
306
307   switch (prop_id)
308     {
309     case PROP_VAL1:
310       g_value_set_int (value, complex->val1);
311       break;
312     case PROP_VAL2:
313       g_value_set_int (value, complex->val2);
314       break;
315     default:
316       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
317       break;
318     }
319 }
320
321 static void
322 complex_object_real_signal (ComplexObject *obj)
323 {
324 }
325
326 static void
327 complex_object_class_init (ComplexObjectClass *class)
328 {
329   GObjectClass *object_class = G_OBJECT_CLASS (class);
330
331   object_class->finalize = complex_object_finalize;
332   object_class->set_property = complex_object_set_property;
333   object_class->get_property = complex_object_get_property;
334   class->signal = complex_object_real_signal;
335
336   complex_signals[COMPLEX_SIGNAL] =
337     g_signal_new ("signal",
338                   G_TYPE_FROM_CLASS (object_class),
339                   G_SIGNAL_RUN_FIRST,
340                   G_STRUCT_OFFSET (ComplexObjectClass, signal),
341                   NULL, NULL,
342                   g_cclosure_marshal_VOID__VOID,
343                   G_TYPE_NONE, 0);
344
345   g_object_class_install_property (object_class,
346                                    PROP_VAL1,
347                                    g_param_spec_int ("val1",
348                                                      "val1",
349                                                      "val1",
350                                                      0,
351                                                      G_MAXINT,
352                                                      42,
353                                                      G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
354   g_object_class_install_property (object_class,
355                                    PROP_VAL2,
356                                    g_param_spec_int ("val2",
357                                                      "val2",
358                                                      "val2",
359                                                      0,
360                                                      G_MAXINT,
361                                                      43,
362                                                      G_PARAM_READWRITE));
363
364
365 }
366
367 static void
368 complex_object_iface_method (TestIface *obj)
369 {
370   ComplexObject *complex = COMPLEX_OBJECT (obj);
371   complex->val1++;
372 }
373
374 static void
375 complex_test_iface_init (gpointer         g_iface,
376                          gpointer         iface_data)
377 {
378   TestIfaceClass *iface = g_iface;
379   iface->method = complex_object_iface_method;
380 }
381
382 static void
383 complex_object_init (ComplexObject *complex_object)
384 {
385   complex_object->val2 = 43;
386 }
387
388 /*************************************************************
389  * Test object construction performance
390  *************************************************************/
391
392 #define NUM_OBJECT_TO_CONSTRUCT 10000
393
394 struct ConstructionTest {
395   GObject **objects;
396   int n_objects;
397   GType type;
398 };
399
400 static gpointer
401 test_construction_setup (PerformanceTest *test)
402 {
403   struct ConstructionTest *data;
404
405   data = g_new0 (struct ConstructionTest, 1);
406   data->type = ((GType (*)())test->extra_data)();
407
408   return data;
409 }
410
411 static void
412 test_construction_init (PerformanceTest *test,
413                         gpointer _data,
414                         double count_factor)
415 {
416   struct ConstructionTest *data = _data;
417   int n;
418
419   n = NUM_OBJECT_TO_CONSTRUCT * count_factor;
420   if (data->n_objects != n)
421     {
422       data->n_objects = n;
423       data->objects = g_new (GObject *, n);
424     }
425 }
426
427 static void
428 test_construction_run (PerformanceTest *test,
429                        gpointer _data)
430 {
431   struct ConstructionTest *data = _data;
432   GObject **objects = data->objects;
433   GType type = data->type;
434   int i, n_objects;
435
436   n_objects = data->n_objects;
437   for (i = 0; i < n_objects; i++)
438     objects[i] = g_object_new (type, NULL);
439 }
440
441 static void
442 test_construction_finish (PerformanceTest *test,
443                           gpointer _data)
444 {
445   struct ConstructionTest *data = _data;
446   int i;
447
448   for (i = 0; i < data->n_objects; i++)
449     g_object_unref (data->objects[i]);
450 }
451
452 static void
453 test_construction_teardown (PerformanceTest *test,
454                             gpointer _data)
455 {
456   struct ConstructionTest *data = _data;
457   g_free (data->objects);
458   g_free (data);
459 }
460
461 static void
462 test_construction_print_result (PerformanceTest *test,
463                                 gpointer _data,
464                                 double time)
465 {
466   struct ConstructionTest *data = _data;
467
468   g_print ("Number of constructed objects per second: %.0f\n",
469            data->n_objects / time);
470 }
471
472 /*************************************************************
473  * Test runtime type check performance
474  *************************************************************/
475
476 #define NUM_KILO_CHECKS_PER_ROUND 50
477
478 struct TypeCheckTest {
479   GObject *object;
480   int n_checks;
481 };
482
483 static gpointer
484 test_type_check_setup (PerformanceTest *test)
485 {
486   struct TypeCheckTest *data;
487
488   data = g_new0 (struct TypeCheckTest, 1);
489   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
490
491   return data;
492 }
493
494 static void
495 test_type_check_init (PerformanceTest *test,
496                       gpointer _data,
497                       double factor)
498 {
499   struct TypeCheckTest *data = _data;
500
501   data->n_checks = factor * NUM_KILO_CHECKS_PER_ROUND;
502 }
503
504
505 /* Work around g_type_check_instance_is_a being marked "pure",
506    and thus only called once for the loop. */
507 gboolean (*my_type_check_instance_is_a) (GTypeInstance *type_instance,
508                                          GType          iface_type) = &g_type_check_instance_is_a;
509
510 static void
511 test_type_check_run (PerformanceTest *test,
512                      gpointer _data)
513 {
514   struct TypeCheckTest *data = _data;
515   volatile GObject *object = data->object;
516   volatile GType type, types[5];
517   int i, j;
518
519   types[0] = test_iface1_get_type ();
520   types[1] = test_iface2_get_type ();
521   types[2] = test_iface3_get_type ();
522   types[3] = test_iface4_get_type ();
523   types[4] = test_iface5_get_type ();
524
525   for (i = 0; i < data->n_checks; i++)
526     {
527       type = types[i%5];
528       for (j = 0; j < 1000; j++)
529         {
530           my_type_check_instance_is_a ((GTypeInstance *)object,
531                                        type);
532         }
533     }
534 }
535
536 static void
537 test_type_check_finish (PerformanceTest *test,
538                         gpointer data)
539 {
540 }
541
542 static void
543 test_type_check_print_result (PerformanceTest *test,
544                               gpointer _data,
545                               double time)
546 {
547   struct TypeCheckTest *data = _data;
548   g_print ("Million type checks per second: %.2f\n",
549            data->n_checks / (1000*time));
550 }
551
552 static void
553 test_type_check_teardown (PerformanceTest *test,
554                           gpointer _data)
555 {
556   struct TypeCheckTest *data = _data;
557
558   g_object_unref (data->object);
559   g_free (data);
560 }
561
562 /*************************************************************
563  * Test signal unhandled emissions performance
564  *************************************************************/
565
566 #define NUM_EMISSIONS_PER_ROUND 10000
567
568 struct EmissionTest {
569   GObject *object;
570   int n_checks;
571 };
572
573 static gpointer
574 test_emission_unhandled_setup (PerformanceTest *test)
575 {
576   struct EmissionTest *data;
577
578   data = g_new0 (struct EmissionTest, 1);
579   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
580
581   return data;
582 }
583
584 static void
585 test_emission_unhandled_init (PerformanceTest *test,
586                               gpointer _data,
587                               double factor)
588 {
589   struct EmissionTest *data = _data;
590
591   data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
592 }
593
594 static void
595 test_emission_unhandled_run (PerformanceTest *test,
596                              gpointer _data)
597 {
598   struct EmissionTest *data = _data;
599   GObject *object = data->object;
600   int i;
601
602   for (i = 0; i < data->n_checks; i++)
603     g_signal_emit (object,
604                    complex_signals[COMPLEX_SIGNAL],
605                    0);
606 }
607
608 static void
609 test_emission_unhandled_finish (PerformanceTest *test,
610                                 gpointer data)
611 {
612 }
613
614 static void
615 test_emission_unhandled_print_result (PerformanceTest *test,
616                                       gpointer _data,
617                                       double time)
618 {
619   struct EmissionTest *data = _data;
620
621   g_print ("Emissions per second: %.0f\n",
622            data->n_checks / time);
623 }
624
625 static void
626 test_emission_unhandled_teardown (PerformanceTest *test,
627                                   gpointer _data)
628 {
629   struct EmissionTest *data = _data;
630
631   g_object_unref (data->object);
632   g_free (data);
633 }
634
635 /*************************************************************
636  * Test signal handled emissions performance
637  *************************************************************/
638
639 static void
640 test_emission_handled_handler (ComplexObject *obj, gpointer data)
641 {
642 }
643
644 static gpointer
645 test_emission_handled_setup (PerformanceTest *test)
646 {
647   struct EmissionTest *data;
648
649   data = g_new0 (struct EmissionTest, 1);
650   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
651   g_signal_connect (data->object, "signal",
652                     G_CALLBACK (test_emission_handled_handler),
653                     NULL);
654
655   return data;
656 }
657
658 static void
659 test_emission_handled_init (PerformanceTest *test,
660                             gpointer _data,
661                             double factor)
662 {
663   struct EmissionTest *data = _data;
664
665   data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
666 }
667
668 static void
669 test_emission_handled_run (PerformanceTest *test,
670                            gpointer _data)
671 {
672   struct EmissionTest *data = _data;
673   GObject *object = data->object;
674   int i;
675
676   for (i = 0; i < data->n_checks; i++)
677     g_signal_emit (object,
678                    complex_signals[COMPLEX_SIGNAL],
679                    0);
680 }
681
682 static void
683 test_emission_handled_finish (PerformanceTest *test,
684                               gpointer data)
685 {
686 }
687
688 static void
689 test_emission_handled_print_result (PerformanceTest *test,
690                                     gpointer _data,
691                                     double time)
692 {
693   struct EmissionTest *data = _data;
694
695   g_print ("Emissions per second: %.0f\n",
696            data->n_checks / time);
697 }
698
699 static void
700 test_emission_handled_teardown (PerformanceTest *test,
701                                 gpointer _data)
702 {
703   struct EmissionTest *data = _data;
704
705   g_object_unref (data->object);
706   g_free (data);
707 }
708
709 /*************************************************************
710  * Main test code
711  *************************************************************/
712
713 static PerformanceTest tests[] = {
714   {
715     "simple-construction",
716     simple_object_get_type,
717     test_construction_setup,
718     test_construction_init,
719     test_construction_run,
720     test_construction_finish,
721     test_construction_teardown,
722     test_construction_print_result
723   },
724   {
725     "complex-construction",
726     complex_object_get_type,
727     test_construction_setup,
728     test_construction_init,
729     test_construction_run,
730     test_construction_finish,
731     test_construction_teardown,
732     test_construction_print_result
733   },
734   {
735     "type-check",
736     NULL,
737     test_type_check_setup,
738     test_type_check_init,
739     test_type_check_run,
740     test_type_check_finish,
741     test_type_check_teardown,
742     test_type_check_print_result
743   },
744   {
745     "emit-unhandled",
746     NULL,
747     test_emission_unhandled_setup,
748     test_emission_unhandled_init,
749     test_emission_unhandled_run,
750     test_emission_unhandled_finish,
751     test_emission_unhandled_teardown,
752     test_emission_unhandled_print_result
753   },
754   {
755     "emit-handled",
756     NULL,
757     test_emission_handled_setup,
758     test_emission_handled_init,
759     test_emission_handled_run,
760     test_emission_handled_finish,
761     test_emission_handled_teardown,
762     test_emission_handled_print_result
763   }
764 };
765
766 static PerformanceTest *
767 find_test (const char *name)
768 {
769   int i;
770   for (i = 0; i < G_N_ELEMENTS (tests); i++)
771     {
772       if (strcmp (tests[i].name, name) == 0)
773         return &tests[i];
774     }
775   return NULL;
776 }
777 int
778 main (int   argc,
779       char *argv[])
780 {
781   PerformanceTest *test;
782   GOptionContext *context;
783   GError *error = NULL;
784   int i;
785
786   g_type_init ();
787
788   context = g_option_context_new ("GObject performance tests");
789   g_option_context_add_main_entries (context, cmd_entries, NULL);
790   if (!g_option_context_parse (context, &argc, &argv, &error))
791     {
792       g_printerr ("%s: %s\n", argv[0], error->message);
793       return 1;
794     }
795
796   if (argc > 1)
797     {
798       for (i = 1; i < argc; i++)
799         {
800           test = find_test (argv[i]);
801           if (test)
802             run_test (test);
803         }
804     }
805   else
806     {
807       for (i = 0; i < G_N_ELEMENTS (tests); i++)
808         run_test (&tests[i]);
809     }
810
811   return 0;
812 }