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