From f4d9789fcfc4c9be497414d6c65476adb9d20236 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 9 Sep 2009 17:08:57 +0200 Subject: [PATCH] Add gobject performance tests for threaded code This measures how much things like lock contention affects the gobject code. --- tests/gobject/Makefile.am | 2 + tests/gobject/performance-threaded.c | 381 +++++++++++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 tests/gobject/performance-threaded.c diff --git a/tests/gobject/Makefile.am b/tests/gobject/Makefile.am index 8a9934c8..62395c02 100644 --- a/tests/gobject/Makefile.am +++ b/tests/gobject/Makefile.am @@ -60,10 +60,12 @@ test_programs = \ ifaceproperties \ override \ performance \ + performance-threaded \ singleton \ references performance_LDADD = $(libgobject) $(libgthread) +performance_threaded_LDADD = $(libgobject) $(libgthread) check_PROGRAMS = $(test_programs) TESTS = $(test_programs) diff --git a/tests/gobject/performance-threaded.c b/tests/gobject/performance-threaded.c new file mode 100644 index 00000000..56a21eb2 --- /dev/null +++ b/tests/gobject/performance-threaded.c @@ -0,0 +1,381 @@ +/* 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 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "testcommon.h" + +#define DEFAULT_TEST_TIME 2 /* seconds */ + +static GType +simple_register_class (const char *name, GType parent, ...) +{ + GInterfaceInfo interface_info = { NULL, NULL, NULL }; + va_list args; + GType type, interface; + + va_start (args, parent); + type = g_type_register_static_simple (parent, name, sizeof (GObjectClass), + NULL, parent == G_TYPE_INTERFACE ? 0 : sizeof (GObject), NULL, 0); + for (;;) + { + interface = va_arg (args, GType); + if (interface == 0) + break; + g_type_add_interface_static (type, interface, &interface_info); + } + va_end (args); + + return type; +} + +/* test emulating liststore behavior for interface lookups */ + +static GType liststore; +static GType liststore_interfaces[6]; + +static gpointer +register_types (void) +{ + static volatile gsize inited = 0; + if (g_once_init_enter (&inited)) + { + liststore_interfaces[0] = simple_register_class ("GtkBuildable", G_TYPE_INTERFACE, 0); + liststore_interfaces[1] = simple_register_class ("GtkTreeDragDest", G_TYPE_INTERFACE, 0); + liststore_interfaces[2] = simple_register_class ("GtkTreeModel", G_TYPE_INTERFACE, 0); + liststore_interfaces[3] = simple_register_class ("GtkTreeDragSource", G_TYPE_INTERFACE, 0); + liststore_interfaces[4] = simple_register_class ("GtkTreeSortable", G_TYPE_INTERFACE, 0); + liststore_interfaces[5] = simple_register_class ("UnrelatedInterface", G_TYPE_INTERFACE, 0); + + liststore = simple_register_class ("GtkListStore", G_TYPE_OBJECT, + liststore_interfaces[0], liststore_interfaces[1], liststore_interfaces[2], + liststore_interfaces[3], liststore_interfaces[4], (GType) 0); + + g_once_init_leave (&inited, 1); + } + return NULL; +} + +static void +liststore_is_a_run (gpointer data) +{ + guint i; + + for (i = 0; i < 1000; i++) + { + g_assert (g_type_is_a (liststore, liststore_interfaces[0])); + g_assert (g_type_is_a (liststore, liststore_interfaces[1])); + g_assert (g_type_is_a (liststore, liststore_interfaces[2])); + g_assert (g_type_is_a (liststore, liststore_interfaces[3])); + g_assert (g_type_is_a (liststore, liststore_interfaces[4])); + g_assert (!g_type_is_a (liststore, liststore_interfaces[5])); + } +} + +static gpointer +liststore_get_class (void) +{ + register_types (); + return g_type_class_ref (liststore); +} + +static void +liststore_interface_peek_run (gpointer klass) +{ + guint i; + gpointer iface; + + for (i = 0; i < 1000; i++) + { + iface = g_type_interface_peek (klass, liststore_interfaces[0]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[1]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[2]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[3]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[4]); + g_assert (iface); + } +} + +static void +liststore_interface_peek_same_run (gpointer klass) +{ + guint i; + gpointer iface; + + for (i = 0; i < 1000; i++) + { + iface = g_type_interface_peek (klass, liststore_interfaces[0]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[0]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[0]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[0]); + g_assert (iface); + iface = g_type_interface_peek (klass, liststore_interfaces[0]); + g_assert (iface); + } +} + +#if 0 +/* DUMB test doing nothing */ + +static gpointer +no_setup (void) +{ + return NULL; +} + +static void +no_run (gpointer data) +{ +} +#endif + +static void +no_reset (gpointer data) +{ +} + +static void +no_teardown (gpointer data) +{ +} + +typedef struct _PerformanceTest PerformanceTest; +struct _PerformanceTest { + const char *name; + + gpointer (*setup) (void); + void (*run) (gpointer data); + void (*reset) (gpointer data); + void (*teardown) (gpointer data); +}; + +static const PerformanceTest tests[] = { + { "liststore-is-a", + register_types, + liststore_is_a_run, + no_reset, + no_teardown }, + { "liststore-interface-peek", + liststore_get_class, + liststore_interface_peek_run, + no_reset, + g_type_class_unref }, + { "liststore-interface-peek-same", + liststore_get_class, + liststore_interface_peek_same_run, + no_reset, + g_type_class_unref }, +#if 0 + { "nothing", + no_setup, + no_run, + no_reset, + no_teardown } +#endif +}; + +static gboolean verbose = FALSE; +static int n_threads = 0; +static gboolean list = FALSE; +static int test_length = DEFAULT_TEST_TIME; + +static GOptionEntry cmd_entries[] = { + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + "Print extra information", NULL}, + {"threads", 't', 0, G_OPTION_ARG_INT, &n_threads, + "number of threads to run in parrallel", NULL}, + {"seconds", 's', 0, G_OPTION_ARG_INT, &test_length, + "Time to run each test in seconds", NULL}, + {"list", 'l', 0, G_OPTION_ARG_NONE, &list, + "List all available tests and exit", NULL}, + {NULL} +}; + +static gpointer +run_test_thread (gpointer user_data) +{ + const PerformanceTest *test = user_data; + gpointer data; + double elapsed; + GTimer *timer, *total; + GArray *results; + + total = g_timer_new (); + g_timer_start (total); + + /* Set up test */ + timer = g_timer_new (); + data = test->setup (); + results = g_array_new (FALSE, FALSE, sizeof (double)); + + /* Run the test */ + while (g_timer_elapsed (total, NULL) < test_length) + { + g_timer_reset (timer); + g_timer_start (timer); + test->run (data); + g_timer_stop (timer); + elapsed = g_timer_elapsed (timer, NULL); + g_array_append_val (results, elapsed); + test->reset (data); + } + + /* Tear down */ + test->teardown (data); + g_timer_destroy (timer); + g_timer_destroy (total); + + return results; +} + +static int +compare_doubles (gconstpointer a, gconstpointer b) +{ + double d = *(double *) a - *(double *) b; + + if (d < 0) + return -1; + if (d > 0) + return 1; + return 0; +} + +static void +print_results (GArray *array) +{ + double min, max, avg; + guint i; + + g_array_sort (array, compare_doubles); + + /* FIXME: discard outliers */ + + min = g_array_index (array, double, 0) * 1000; + max = g_array_index (array, double, array->len - 1) * 1000; + avg = 0; + for (i = 0; i < array->len; i++) + { + avg += g_array_index (array, double, i); + } + avg = avg / array->len * 1000; + + g_print (" %u runs, min/avg/max = %.3f/%.3f/%.3f ms\n", array->len, min, avg, max); +} + +static void +run_test (const PerformanceTest *test) +{ + GArray *results; + + g_print ("Running test \"%s\"\n", test->name); + + if (n_threads == 0) { + results = run_test_thread ((gpointer) test); + } else { + guint i; + GThread **threads; + GArray *thread_results; + + threads = g_new (GThread *, n_threads); + for (i = 0; i < n_threads; i++) { + threads[i] = g_thread_create (run_test_thread, (gpointer) test, TRUE, NULL); + g_assert (threads[i] != NULL); + } + + results = g_array_new (FALSE, FALSE, sizeof (double)); + for (i = 0; i < n_threads; i++) { + thread_results = g_thread_join (threads[i]); + g_array_append_vals (results, thread_results->data, thread_results->len); + g_array_free (thread_results, TRUE); + } + g_free (threads); + } + + print_results (results); + g_array_free (results, TRUE); +} + +static const PerformanceTest * +find_test (const char *name) +{ + int 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[]) +{ + const PerformanceTest *test; + GOptionContext *context; + GError *error = NULL; + int i; + + g_type_init (); + + 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 (list) + { + for (i = 0; i < G_N_ELEMENTS (tests); i++) + { + g_print ("%s\n", tests[i].name); + } + return 0; + } + + if (n_threads) + g_thread_init (NULL); + + if (argc > 1) + { + for (i = 1; i < argc; i++) + { + test = find_test (argv[i]); + if (test) + run_test (test); + } + } + else + { + for (i = 0; i < G_N_ELEMENTS (tests); i++) + run_test (&tests[i]); + } + + return 0; +} -- 2.34.1