mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 10:08:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			382 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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;
 | 
						|
}
 |