Add gobject performance tests for threaded code
authorAlexander Larsson <alexl@redhat.com>
Wed, 9 Sep 2009 15:08:57 +0000 (17:08 +0200)
committerAlexander Larsson <alexl@redhat.com>
Fri, 2 Oct 2009 19:02:48 +0000 (21:02 +0200)
This measures how much things like lock contention affects the gobject
code.

tests/gobject/Makefile.am
tests/gobject/performance-threaded.c [new file with mode: 0644]

index 8a9934c..62395c0 100644 (file)
@@ -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 (file)
index 0000000..56a21eb
--- /dev/null
@@ -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 <math.h>
+#include <string.h>
+#include <glib-object.h>
+#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;
+}