Initial commit
[platform/upstream/glib2.0.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 emissions performance
564  *************************************************************/
565
566 #define NUM_EMISSIONS_PER_ROUND 10000
567
568 struct EmissionTest {
569   GObject *object;
570   int n_checks;
571 };
572 static gpointer
573 test_emission_setup (PerformanceTest *test)
574 {
575   struct EmissionTest *data;
576
577   data = g_new0 (struct EmissionTest, 1);
578   data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL);
579
580   return data;
581 }
582
583 static void
584 test_emission_init (PerformanceTest *test,
585                     gpointer _data,
586                     double factor)
587 {
588   struct EmissionTest *data = _data;
589
590   data->n_checks = factor * NUM_EMISSIONS_PER_ROUND;
591 }
592
593 static void
594 test_emission_run (PerformanceTest *test,
595                    gpointer _data)
596 {
597   struct EmissionTest *data = _data;
598   GObject *object = data->object;
599   int i;
600
601   for (i = 0; i < data->n_checks; i++)
602     g_signal_emit (object,
603                    complex_signals[COMPLEX_SIGNAL],
604                    0);
605 }
606
607 static void
608 test_emission_finish (PerformanceTest *test,
609                       gpointer data)
610 {
611 }
612
613 static void
614 test_emission_print_result (PerformanceTest *test,
615                             gpointer _data,
616                             double time)
617 {
618   struct EmissionTest *data = _data;
619
620   g_print ("Emissions per second: %.0f\n",
621            data->n_checks / time);
622 }
623
624 static void
625 test_emission_teardown (PerformanceTest *test,
626                         gpointer _data)
627 {
628   struct EmissionTest *data = _data;
629
630   g_object_unref (data->object);
631   g_free (data);
632 }
633
634
635
636 /*************************************************************
637  * Main test code
638  *************************************************************/
639
640 static PerformanceTest tests[] = {
641   {
642     "simple-construction",
643     simple_object_get_type,
644     test_construction_setup,
645     test_construction_init,
646     test_construction_run,
647     test_construction_finish,
648     test_construction_teardown,
649     test_construction_print_result
650   },
651   {
652     "complex-construction",
653     complex_object_get_type,
654     test_construction_setup,
655     test_construction_init,
656     test_construction_run,
657     test_construction_finish,
658     test_construction_teardown,
659     test_construction_print_result
660   },
661   {
662     "type-check",
663     NULL,
664     test_type_check_setup,
665     test_type_check_init,
666     test_type_check_run,
667     test_type_check_finish,
668     test_type_check_teardown,
669     test_type_check_print_result
670   },
671   {
672     "emit",
673     NULL,
674     test_emission_setup,
675     test_emission_init,
676     test_emission_run,
677     test_emission_finish,
678     test_emission_teardown,
679     test_emission_print_result
680   }
681 };
682
683 static PerformanceTest *
684 find_test (const char *name)
685 {
686   int i;
687   for (i = 0; i < G_N_ELEMENTS (tests); i++)
688     {
689       if (strcmp (tests[i].name, name) == 0)
690         return &tests[i];
691     }
692   return NULL;
693 }
694 int
695 main (int   argc,
696       char *argv[])
697 {
698   PerformanceTest *test;
699   GOptionContext *context;
700   GError *error = NULL;
701   int i;
702
703   g_type_init ();
704
705   context = g_option_context_new ("GObject performance tests");
706   g_option_context_add_main_entries (context, cmd_entries, NULL);
707   if (!g_option_context_parse (context, &argc, &argv, &error))
708     {
709       g_printerr ("%s: %s\n", argv[0], error->message);
710       return 1;
711     }
712
713   if (argc > 1)
714     {
715       for (i = 1; i < argc; i++)
716         {
717           test = find_test (argv[i]);
718           if (test)
719             run_test (test);
720         }
721     }
722   else
723     {
724       for (i = 0; i < G_N_ELEMENTS (tests); i++)
725         run_test (&tests[i]);
726     }
727
728   return 0;
729 }