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