diff options
Diffstat (limited to 'gobject/tests/performance/performance.c')
-rw-r--r-- | gobject/tests/performance/performance.c | 1223 |
1 files changed, 1223 insertions, 0 deletions
diff --git a/gobject/tests/performance/performance.c b/gobject/tests/performance/performance.c new file mode 100644 index 000000000..937509d8d --- /dev/null +++ b/gobject/tests/performance/performance.c @@ -0,0 +1,1223 @@ +/* GObject - GLib Type, Object, Parameter and Signal Library + * Copyright (C) 2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <math.h> +#include <string.h> +#include <glib-object.h> +#include "../testcommon.h" + +#define WARM_UP_N_RUNS 50 +#define ESTIMATE_ROUND_TIME_N_RUNS 5 +#define DEFAULT_TEST_TIME 15 /* seconds */ + /* The time we want each round to take, in seconds, this should + * be large enough compared to the timer resolution, but small + * enough that the risk of any random slowness will miss the + * running window */ +#define TARGET_ROUND_TIME 0.008 + +static gboolean verbose = FALSE; +static int test_length = DEFAULT_TEST_TIME; + +static GOptionEntry cmd_entries[] = { + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + "Print extra information", NULL}, + {"seconds", 's', 0, G_OPTION_ARG_INT, &test_length, + "Time to run each test in seconds", NULL}, + G_OPTION_ENTRY_NULL +}; + +typedef struct _PerformanceTest PerformanceTest; +struct _PerformanceTest { + const char *name; + gpointer extra_data; + + gpointer (*setup) (PerformanceTest *test); + void (*init) (PerformanceTest *test, + gpointer data, + double factor); + void (*run) (PerformanceTest *test, + gpointer data); + void (*finish) (PerformanceTest *test, + gpointer data); + void (*teardown) (PerformanceTest *test, + gpointer data); + void (*print_result) (PerformanceTest *test, + gpointer data, + double time); +}; + +static void +run_test (PerformanceTest *test) +{ + gpointer data = NULL; + guint64 i, num_rounds; + double elapsed, min_elapsed, max_elapsed, avg_elapsed, factor; + GTimer *timer; + + g_print ("Running test %s\n", test->name); + + /* Set up test */ + timer = g_timer_new (); + data = test->setup (test); + + if (verbose) + g_print ("Warming up\n"); + + g_timer_start (timer); + + /* Warm up the test by doing a few runs */ + for (i = 0; i < WARM_UP_N_RUNS; i++) + { + test->init (test, data, 1.0); + test->run (test, data); + test->finish (test, data); + } + + g_timer_stop (timer); + elapsed = g_timer_elapsed (timer, NULL); + + if (verbose) + { + g_print ("Warm up time: %.2f secs\n", elapsed); + g_print ("Estimating round time\n"); + } + + /* Estimate time for one run by doing a few test rounds */ + min_elapsed = 0; + for (i = 0; i < ESTIMATE_ROUND_TIME_N_RUNS; i++) + { + test->init (test, data, 1.0); + g_timer_start (timer); + test->run (test, data); + g_timer_stop (timer); + test->finish (test, data); + + elapsed = g_timer_elapsed (timer, NULL); + if (i == 0) + min_elapsed = elapsed; + else + min_elapsed = MIN (min_elapsed, elapsed); + } + + factor = TARGET_ROUND_TIME / min_elapsed; + + if (verbose) + g_print ("Uncorrected round time: %.4f msecs, correction factor %.2f\n", 1000*min_elapsed, factor); + + /* Calculate number of rounds needed */ + num_rounds = (test_length / TARGET_ROUND_TIME) + 1; + + if (verbose) + g_print ("Running %"G_GINT64_MODIFIER"d rounds\n", num_rounds); + + /* Run the test */ + avg_elapsed = 0.0; + min_elapsed = 0.0; + max_elapsed = 0.0; + for (i = 0; i < num_rounds; i++) + { + test->init (test, data, factor); + g_timer_start (timer); + test->run (test, data); + g_timer_stop (timer); + test->finish (test, data); + elapsed = g_timer_elapsed (timer, NULL); + + if (i == 0) + max_elapsed = min_elapsed = avg_elapsed = elapsed; + else + { + min_elapsed = MIN (min_elapsed, elapsed); + max_elapsed = MAX (max_elapsed, elapsed); + avg_elapsed += elapsed; + } + } + + if (num_rounds > 1) + avg_elapsed = avg_elapsed / num_rounds; + + if (verbose) + { + g_print ("Minimum corrected round time: %.2f msecs\n", min_elapsed * 1000); + g_print ("Maximum corrected round time: %.2f msecs\n", max_elapsed * 1000); + g_print ("Average corrected round time: %.2f msecs\n", avg_elapsed * 1000); + } + + /* Print the results */ + test->print_result (test, data, min_elapsed); + + /* Tear down */ + test->teardown (test, data); + g_timer_destroy (timer); +} + +/************************************************************* + * Simple object is a very simple small GObject subclass + * with no properties, no signals, implementing no interfaces + *************************************************************/ + +static GType simple_object_get_type (void); +#define SIMPLE_TYPE_OBJECT (simple_object_get_type ()) +typedef struct _SimpleObject SimpleObject; +typedef struct _SimpleObjectClass SimpleObjectClass; + +struct _SimpleObject +{ + GObject parent_instance; + int val; +}; + +struct _SimpleObjectClass +{ + GObjectClass parent_class; +}; + +G_DEFINE_TYPE (SimpleObject, simple_object, G_TYPE_OBJECT) + +static void +simple_object_finalize (GObject *object) +{ + G_OBJECT_CLASS (simple_object_parent_class)->finalize (object); +} + +static void +simple_object_class_init (SimpleObjectClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = simple_object_finalize; +} + +static void +simple_object_init (SimpleObject *simple_object) +{ + simple_object->val = 42; +} + +typedef struct _TestIfaceClass TestIfaceClass; +typedef struct _TestIfaceClass TestIface1Class; +typedef struct _TestIfaceClass TestIface2Class; +typedef struct _TestIfaceClass TestIface3Class; +typedef struct _TestIfaceClass TestIface4Class; +typedef struct _TestIfaceClass TestIface5Class; +typedef struct _TestIface TestIface; + +struct _TestIfaceClass +{ + GTypeInterface base_iface; + void (*method) (TestIface *obj); +}; + +static GType test_iface1_get_type (void); +static GType test_iface2_get_type (void); +static GType test_iface3_get_type (void); +static GType test_iface4_get_type (void); +static GType test_iface5_get_type (void); + +#define TEST_TYPE_IFACE1 (test_iface1_get_type ()) +#define TEST_TYPE_IFACE2 (test_iface2_get_type ()) +#define TEST_TYPE_IFACE3 (test_iface3_get_type ()) +#define TEST_TYPE_IFACE4 (test_iface4_get_type ()) +#define TEST_TYPE_IFACE5 (test_iface5_get_type ()) + +static DEFINE_IFACE (TestIface1, test_iface1, NULL, NULL) +static DEFINE_IFACE (TestIface2, test_iface2, NULL, NULL) +static DEFINE_IFACE (TestIface3, test_iface3, NULL, NULL) +static DEFINE_IFACE (TestIface4, test_iface4, NULL, NULL) +static DEFINE_IFACE (TestIface5, test_iface5, NULL, NULL) + +/************************************************************* + * Complex object is a GObject subclass with a properties, + * construct properties, signals and implementing an interface. + *************************************************************/ + +static GType complex_object_get_type (void); +#define COMPLEX_TYPE_OBJECT (complex_object_get_type ()) +typedef struct _ComplexObject ComplexObject; +typedef struct _ComplexObjectClass ComplexObjectClass; + +struct _ComplexObject +{ + GObject parent_instance; + int val1; + char *val2; +}; + +struct _ComplexObjectClass +{ + GObjectClass parent_class; + + void (*signal) (ComplexObject *obj); + void (*signal_empty) (ComplexObject *obj); +}; + +static void complex_test_iface_init (gpointer g_iface, + gpointer iface_data); + +G_DEFINE_TYPE_EXTENDED (ComplexObject, complex_object, + G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE1, complex_test_iface_init) + G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE2, complex_test_iface_init) + G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE3, complex_test_iface_init) + G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE4, complex_test_iface_init) + G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE5, complex_test_iface_init)) + +#define COMPLEX_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), COMPLEX_TYPE_OBJECT, ComplexObject)) + +enum { + PROP_0, + PROP_VAL1, + PROP_VAL2, + N_PROPERTIES +}; + +static GParamSpec *pspecs[N_PROPERTIES] = { NULL, }; + +enum { + COMPLEX_SIGNAL, + COMPLEX_SIGNAL_EMPTY, + COMPLEX_SIGNAL_GENERIC, + COMPLEX_SIGNAL_GENERIC_EMPTY, + COMPLEX_SIGNAL_ARGS, + COMPLEX_LAST_SIGNAL +}; + +static guint complex_signals[COMPLEX_LAST_SIGNAL] = { 0 }; + +static void +complex_object_finalize (GObject *object) +{ + ComplexObject *c = COMPLEX_OBJECT (object); + + g_free (c->val2); + + G_OBJECT_CLASS (complex_object_parent_class)->finalize (object); +} + +static void +complex_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ComplexObject *complex = COMPLEX_OBJECT (object); + + switch (prop_id) + { + case PROP_VAL1: + complex->val1 = g_value_get_int (value); + break; + case PROP_VAL2: + g_free (complex->val2); + complex->val2 = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +complex_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ComplexObject *complex = COMPLEX_OBJECT (object); + + switch (prop_id) + { + case PROP_VAL1: + g_value_set_int (value, complex->val1); + break; + case PROP_VAL2: + g_value_set_string (value, complex->val2); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +complex_object_real_signal (ComplexObject *obj) +{ +} + +static void +complex_object_class_init (ComplexObjectClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = complex_object_finalize; + object_class->set_property = complex_object_set_property; + object_class->get_property = complex_object_get_property; + + class->signal = complex_object_real_signal; + + complex_signals[COMPLEX_SIGNAL] = + g_signal_new ("signal", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ComplexObjectClass, signal), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + complex_signals[COMPLEX_SIGNAL_EMPTY] = + g_signal_new ("signal-empty", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ComplexObjectClass, signal_empty), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + complex_signals[COMPLEX_SIGNAL_GENERIC] = + g_signal_new ("signal-generic", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ComplexObjectClass, signal), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + complex_signals[COMPLEX_SIGNAL_GENERIC_EMPTY] = + g_signal_new ("signal-generic-empty", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ComplexObjectClass, signal_empty), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + complex_signals[COMPLEX_SIGNAL_ARGS] = + g_signal_new ("signal-args", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ComplexObjectClass, signal), + NULL, NULL, + g_cclosure_marshal_VOID__UINT_POINTER, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER); + + pspecs[PROP_VAL1] = g_param_spec_int ("val1", "val1", "val1", + 0, G_MAXINT, 42, + G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + pspecs[PROP_VAL2] = g_param_spec_string ("val2", "val2", "val2", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, N_PROPERTIES, pspecs); +} + +static void +complex_object_iface_method (TestIface *obj) +{ + ComplexObject *complex = COMPLEX_OBJECT (obj); + complex->val1++; +} + +static void +complex_test_iface_init (gpointer g_iface, + gpointer iface_data) +{ + TestIfaceClass *iface = g_iface; + iface->method = complex_object_iface_method; +} + +static void +complex_object_init (ComplexObject *complex_object) +{ + complex_object->val1 = 42; +} + +/************************************************************* + * Test object construction performance + *************************************************************/ + +#define NUM_OBJECT_TO_CONSTRUCT 10000 + +struct ConstructionTest { + GObject **objects; + int n_objects; + GType type; +}; + +static gpointer +test_construction_setup (PerformanceTest *test) +{ + struct ConstructionTest *data; + + data = g_new0 (struct ConstructionTest, 1); + data->type = ((GType (*)(void))test->extra_data)(); + + return data; +} + +static void +test_construction_init (PerformanceTest *test, + gpointer _data, + double count_factor) +{ + struct ConstructionTest *data = _data; + int n; + + n = NUM_OBJECT_TO_CONSTRUCT * count_factor; + if (data->n_objects != n) + { + data->n_objects = n; + data->objects = g_new (GObject *, n); + } +} + +static void +test_construction_run (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + GObject **objects = data->objects; + GType type = data->type; + int i, n_objects; + + n_objects = data->n_objects; + for (i = 0; i < n_objects; i++) + objects[i] = g_object_new (type, NULL); +} + +static void +test_construction_run1 (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + GObject **objects = data->objects; + int i, n_objects; + + n_objects = data->n_objects; + for (i = 0; i < n_objects; i++) + objects[i] = (GObject *) g_slice_new0 (SimpleObject); +} + +static void +test_complex_construction_run (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + GObject **objects = data->objects; + GType type = data->type; + int i, n_objects; + + n_objects = data->n_objects; + for (i = 0; i < n_objects; i++) + objects[i] = g_object_new (type, "val1", 5, "val2", "thousand", NULL); +} + +static void +test_complex_construction_run1 (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + GObject **objects = data->objects; + GType type = data->type; + int i, n_objects; + + n_objects = data->n_objects; + for (i = 0; i < n_objects; i++) + { + ComplexObject *object; + object = (ComplexObject *)g_object_new (type, NULL); + object->val1 = 5; + object->val2 = g_strdup ("thousand"); + objects[i] = (GObject *)object; + } +} + +static void +test_complex_construction_run2 (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + GObject **objects = data->objects; + GType type = data->type; + int i, n_objects; + + n_objects = data->n_objects; + for (i = 0; i < n_objects; i++) + { + objects[i] = g_object_new (type, NULL); + } +} + +static void +test_construction_finish (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + int i; + + for (i = 0; i < data->n_objects; i++) + g_object_unref (data->objects[i]); +} + +static void +test_construction_finish1 (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + int i; + + for (i = 0; i < data->n_objects; i++) + g_slice_free (SimpleObject, (SimpleObject *)data->objects[i]); +} + +static void +test_construction_teardown (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + g_free (data->objects); + g_free (data); +} + +static void +test_finalization_init (PerformanceTest *test, + gpointer _data, + double count_factor) +{ + struct ConstructionTest *data = _data; + int n; + + n = NUM_OBJECT_TO_CONSTRUCT * count_factor; + if (data->n_objects != n) + { + data->n_objects = n; + data->objects = g_new (GObject *, n); + } + + for (int i = 0; i < data->n_objects; i++) + { + data->objects[i] = g_object_new (data->type, NULL); + } +} + +static void +test_finalization_run (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + GObject **objects = data->objects; + int i, n_objects; + + n_objects = data->n_objects; + for (i = 0; i < n_objects; i++) + { + g_object_unref (objects[i]); + } +} + +static void +test_finalization_finish (PerformanceTest *test, + gpointer _data) +{ +} + +static void +test_construction_print_result (PerformanceTest *test, + gpointer _data, + double time) +{ + struct ConstructionTest *data = _data; + + g_print ("Millions of constructed objects per second: %.3f\n", + data->n_objects / (time * 1000000)); +} + +static void +test_finalization_print_result (PerformanceTest *test, + gpointer _data, + double time) +{ + struct ConstructionTest *data = _data; + + g_print ("Millions of finalized objects per second: %.3f\n", + data->n_objects / (time * 1000000)); +} + +/************************************************************* + * Test runtime type check performance + *************************************************************/ + +#define NUM_KILO_CHECKS_PER_ROUND 50 + +struct TypeCheckTest { + GObject *object; + int n_checks; +}; + +static gpointer +test_type_check_setup (PerformanceTest *test) +{ + struct TypeCheckTest *data; + + data = g_new0 (struct TypeCheckTest, 1); + data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL); + + return data; +} + +static void +test_type_check_init (PerformanceTest *test, + gpointer _data, + double factor) +{ + struct TypeCheckTest *data = _data; + + data->n_checks = factor * NUM_KILO_CHECKS_PER_ROUND; +} + + +/* Work around g_type_check_instance_is_a being marked "pure", + and thus only called once for the loop. */ +gboolean (*my_type_check_instance_is_a) (GTypeInstance *type_instance, + GType iface_type) = &g_type_check_instance_is_a; + +static void +test_type_check_run (PerformanceTest *test, + gpointer _data) +{ + struct TypeCheckTest *data = _data; + GObject *object = data->object; + GType type, types[5]; + int i, j; + + types[0] = test_iface1_get_type (); + types[1] = test_iface2_get_type (); + types[2] = test_iface3_get_type (); + types[3] = test_iface4_get_type (); + types[4] = test_iface5_get_type (); + + for (i = 0; i < data->n_checks; i++) + { + type = types[i%5]; + for (j = 0; j < 1000; j++) + { + my_type_check_instance_is_a ((GTypeInstance *)object, + type); + } + } +} + +static void +test_type_check_finish (PerformanceTest *test, + gpointer data) +{ +} + +static void +test_type_check_print_result (PerformanceTest *test, + gpointer _data, + double time) +{ + struct TypeCheckTest *data = _data; + g_print ("Million type checks per second: %.2f\n", + data->n_checks / (1000*time)); +} + +static void +test_type_check_teardown (PerformanceTest *test, + gpointer _data) +{ + struct TypeCheckTest *data = _data; + + g_object_unref (data->object); + g_free (data); +} + +/************************************************************* + * Test signal emissions performance (common code) + *************************************************************/ + +#define NUM_EMISSIONS_PER_ROUND 10000 + +struct EmissionTest { + GObject *object; + int n_checks; + int signal_id; +}; + +static void +test_emission_run (PerformanceTest *test, + gpointer _data) +{ + struct EmissionTest *data = _data; + GObject *object = data->object; + int i; + + for (i = 0; i < data->n_checks; i++) + g_signal_emit (object, data->signal_id, 0); +} + +static void +test_emission_run_args (PerformanceTest *test, + gpointer _data) +{ + struct EmissionTest *data = _data; + GObject *object = data->object; + int i; + + for (i = 0; i < data->n_checks; i++) + g_signal_emit (object, data->signal_id, 0, 0, NULL); +} + +/************************************************************* + * Test signal unhandled emissions performance + *************************************************************/ + +static gpointer +test_emission_unhandled_setup (PerformanceTest *test) +{ + struct EmissionTest *data; + + data = g_new0 (struct EmissionTest, 1); + data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL); + data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)]; + return data; +} + +static void +test_emission_unhandled_init (PerformanceTest *test, + gpointer _data, + double factor) +{ + struct EmissionTest *data = _data; + + data->n_checks = factor * NUM_EMISSIONS_PER_ROUND; +} + +static void +test_emission_unhandled_finish (PerformanceTest *test, + gpointer data) +{ +} + +static void +test_emission_unhandled_print_result (PerformanceTest *test, + gpointer _data, + double time) +{ + struct EmissionTest *data = _data; + + g_print ("Emissions per second: %.0f\n", + data->n_checks / time); +} + +static void +test_emission_unhandled_teardown (PerformanceTest *test, + gpointer _data) +{ + struct EmissionTest *data = _data; + + g_object_unref (data->object); + g_free (data); +} + +/************************************************************* + * Test signal handled emissions performance + *************************************************************/ + +static void +test_emission_handled_handler (ComplexObject *obj, gpointer data) +{ +} + +static gpointer +test_emission_handled_setup (PerformanceTest *test) +{ + struct EmissionTest *data; + + data = g_new0 (struct EmissionTest, 1); + data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL); + data->signal_id = complex_signals[GPOINTER_TO_INT (test->extra_data)]; + g_signal_connect (data->object, "signal", + G_CALLBACK (test_emission_handled_handler), + NULL); + g_signal_connect (data->object, "signal-empty", + G_CALLBACK (test_emission_handled_handler), + NULL); + g_signal_connect (data->object, "signal-generic", + G_CALLBACK (test_emission_handled_handler), + NULL); + g_signal_connect (data->object, "signal-generic-empty", + G_CALLBACK (test_emission_handled_handler), + NULL); + g_signal_connect (data->object, "signal-args", + G_CALLBACK (test_emission_handled_handler), + NULL); + + return data; +} + +static void +test_emission_handled_init (PerformanceTest *test, + gpointer _data, + double factor) +{ + struct EmissionTest *data = _data; + + data->n_checks = factor * NUM_EMISSIONS_PER_ROUND; +} + +static void +test_emission_handled_finish (PerformanceTest *test, + gpointer data) +{ +} + +static void +test_emission_handled_print_result (PerformanceTest *test, + gpointer _data, + double time) +{ + struct EmissionTest *data = _data; + + g_print ("Emissions per second: %.0f\n", + data->n_checks / time); +} + +static void +test_emission_handled_teardown (PerformanceTest *test, + gpointer _data) +{ + struct EmissionTest *data = _data; + + g_object_unref (data->object); + g_free (data); +} + +/************************************************************* + * Test object refcount performance + *************************************************************/ + +#define NUM_KILO_REFS_PER_ROUND 100000 + +struct RefcountTest { + GObject *object; + int n_checks; +}; + +static gpointer +test_refcount_setup (PerformanceTest *test) +{ + struct RefcountTest *data; + + data = g_new0 (struct RefcountTest, 1); + data->object = g_object_new (COMPLEX_TYPE_OBJECT, NULL); + + return data; +} + +static void +test_refcount_init (PerformanceTest *test, + gpointer _data, + double factor) +{ + struct RefcountTest *data = _data; + + data->n_checks = factor * NUM_KILO_REFS_PER_ROUND; +} + +static void +test_refcount_run (PerformanceTest *test, + gpointer _data) +{ + struct RefcountTest *data = _data; + GObject *object = data->object; + int i; + + for (i = 0; i < data->n_checks; i++) + { + g_object_ref (object); + g_object_ref (object); + g_object_ref (object); + g_object_unref (object); + g_object_unref (object); + + g_object_ref (object); + g_object_ref (object); + g_object_unref (object); + g_object_unref (object); + g_object_unref (object); + } +} + +static void +test_refcount_finish (PerformanceTest *test, + gpointer _data) +{ +} + +static void +test_refcount_print_result (PerformanceTest *test, + gpointer _data, + double time) +{ + struct RefcountTest *data = _data; + g_print ("Million refs+unref per second: %.2f\n", + data->n_checks * 5 / (time * 1000000 )); +} + +static void +test_refcount_teardown (PerformanceTest *test, + gpointer _data) +{ + struct RefcountTest *data = _data; + + g_object_unref (data->object); + g_free (data); +} + +/************************************************************* + * Main test code + *************************************************************/ + +static PerformanceTest tests[] = { + { + "simple-construction", + simple_object_get_type, + test_construction_setup, + test_construction_init, + test_construction_run, + test_construction_finish, + test_construction_teardown, + test_construction_print_result + }, + { + "simple-construction1", + simple_object_get_type, + test_construction_setup, + test_construction_init, + test_construction_run1, + test_construction_finish1, + test_construction_teardown, + test_construction_print_result + }, + { + "complex-construction", + complex_object_get_type, + test_construction_setup, + test_construction_init, + test_complex_construction_run, + test_construction_finish, + test_construction_teardown, + test_construction_print_result + }, + { + "complex-construction1", + complex_object_get_type, + test_construction_setup, + test_construction_init, + test_complex_construction_run1, + test_construction_finish, + test_construction_teardown, + test_construction_print_result + }, + { + "complex-construction2", + complex_object_get_type, + test_construction_setup, + test_construction_init, + test_complex_construction_run2, + test_construction_finish, + test_construction_teardown, + test_construction_print_result + }, + { + "finalization", + simple_object_get_type, + test_construction_setup, + test_finalization_init, + test_finalization_run, + test_finalization_finish, + test_construction_teardown, + test_finalization_print_result + }, + { + "type-check", + NULL, + test_type_check_setup, + test_type_check_init, + test_type_check_run, + test_type_check_finish, + test_type_check_teardown, + test_type_check_print_result + }, + { + "emit-unhandled", + GINT_TO_POINTER (COMPLEX_SIGNAL), + test_emission_unhandled_setup, + test_emission_unhandled_init, + test_emission_run, + test_emission_unhandled_finish, + test_emission_unhandled_teardown, + test_emission_unhandled_print_result + }, + { + "emit-unhandled-empty", + GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY), + test_emission_unhandled_setup, + test_emission_unhandled_init, + test_emission_run, + test_emission_unhandled_finish, + test_emission_unhandled_teardown, + test_emission_unhandled_print_result + }, + { + "emit-unhandled-generic", + GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC), + test_emission_unhandled_setup, + test_emission_unhandled_init, + test_emission_run, + test_emission_unhandled_finish, + test_emission_unhandled_teardown, + test_emission_unhandled_print_result + }, + { + "emit-unhandled-generic-empty", + GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY), + test_emission_unhandled_setup, + test_emission_unhandled_init, + test_emission_run, + test_emission_unhandled_finish, + test_emission_unhandled_teardown, + test_emission_unhandled_print_result + }, + { + "emit-unhandled-args", + GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS), + test_emission_unhandled_setup, + test_emission_unhandled_init, + test_emission_run_args, + test_emission_unhandled_finish, + test_emission_unhandled_teardown, + test_emission_unhandled_print_result + }, + { + "emit-handled", + GINT_TO_POINTER (COMPLEX_SIGNAL), + test_emission_handled_setup, + test_emission_handled_init, + test_emission_run, + test_emission_handled_finish, + test_emission_handled_teardown, + test_emission_handled_print_result + }, + { + "emit-handled-empty", + GINT_TO_POINTER (COMPLEX_SIGNAL_EMPTY), + test_emission_handled_setup, + test_emission_handled_init, + test_emission_run, + test_emission_handled_finish, + test_emission_handled_teardown, + test_emission_handled_print_result + }, + { + "emit-handled-generic", + GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC), + test_emission_handled_setup, + test_emission_handled_init, + test_emission_run, + test_emission_handled_finish, + test_emission_handled_teardown, + test_emission_handled_print_result + }, + { + "emit-handled-generic-empty", + GINT_TO_POINTER (COMPLEX_SIGNAL_GENERIC_EMPTY), + test_emission_handled_setup, + test_emission_handled_init, + test_emission_run, + test_emission_handled_finish, + test_emission_handled_teardown, + test_emission_handled_print_result + }, + { + "emit-handled-args", + GINT_TO_POINTER (COMPLEX_SIGNAL_ARGS), + test_emission_handled_setup, + test_emission_handled_init, + test_emission_run_args, + test_emission_handled_finish, + test_emission_handled_teardown, + test_emission_handled_print_result + }, + { + "refcount", + NULL, + test_refcount_setup, + test_refcount_init, + test_refcount_run, + test_refcount_finish, + test_refcount_teardown, + test_refcount_print_result + } +}; + +static PerformanceTest * +find_test (const char *name) +{ + gsize i; + for (i = 0; i < G_N_ELEMENTS (tests); i++) + { + if (strcmp (tests[i].name, name) == 0) + return &tests[i]; + } + return NULL; +} +int +main (int argc, + char *argv[]) +{ + PerformanceTest *test; + GOptionContext *context; + GError *error = NULL; + int i; + + context = g_option_context_new ("GObject performance tests"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (argc > 1) + { + for (i = 1; i < argc; i++) + { + test = find_test (argv[i]); + if (test) + run_test (test); + } + } + else + { + gsize k; + for (k = 0; k < G_N_ELEMENTS (tests); k++) + run_test (&tests[k]); + } + + return 0; +} |