Add benchmarks
This commit is contained in:
156
benchmarks/benchmark.h
Normal file
156
benchmarks/benchmark.h
Normal file
@@ -0,0 +1,156 @@
|
||||
|
||||
#ifndef __GTK_BENCHMARK_H__
|
||||
#define __GTK_BENCHMARK_H__
|
||||
|
||||
#include <valgrind/callgrind.h>
|
||||
#include <glib.h>
|
||||
|
||||
#define SAMPLE_SIZE 5
|
||||
|
||||
|
||||
typedef struct _Benchmark Benchmark;
|
||||
typedef void (*BenchmarkFunc)(Benchmark *b, gsize size, gpointer user_data);
|
||||
|
||||
struct _Benchmark
|
||||
{
|
||||
char *name;
|
||||
gint64 start_time;
|
||||
gint64 end_time;
|
||||
gsize size;
|
||||
BenchmarkFunc func;
|
||||
guint profile : 1;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
static void
|
||||
benchmark_destroy (Benchmark *b)
|
||||
{
|
||||
g_free (b->name);
|
||||
}
|
||||
|
||||
static void
|
||||
benchmark_start (Benchmark *b)
|
||||
{
|
||||
b->start_time = g_get_monotonic_time ();
|
||||
|
||||
if (b->profile)
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
}
|
||||
|
||||
static void
|
||||
benchmark_stop (Benchmark *b)
|
||||
{
|
||||
if (b->profile)
|
||||
CALLGRIND_STOP_INSTRUMENTATION;
|
||||
|
||||
b->end_time = g_get_monotonic_time ();
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GArray *benchmarks;
|
||||
char *profile_benchmark_name;
|
||||
} BenchmarkSuite;
|
||||
|
||||
static void
|
||||
benchmark_suite_init (BenchmarkSuite *bs,
|
||||
const char *profile_benchmark_name)
|
||||
{
|
||||
bs->benchmarks = g_array_new (FALSE, TRUE, sizeof (Benchmark));
|
||||
g_array_set_clear_func (bs->benchmarks, (GDestroyNotify)benchmark_destroy);
|
||||
bs->profile_benchmark_name = (char *)profile_benchmark_name; // XXX strdup
|
||||
|
||||
g_assert (SAMPLE_SIZE % 2 == 1);
|
||||
}
|
||||
|
||||
static void
|
||||
benchmark_suite_add (BenchmarkSuite *bs,
|
||||
const char *benchmark_name,
|
||||
gsize size,
|
||||
BenchmarkFunc benchmark_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
Benchmark *b;
|
||||
|
||||
g_array_set_size (bs->benchmarks, bs->benchmarks->len + 1);
|
||||
b = &g_array_index (bs->benchmarks, Benchmark, bs->benchmarks->len - 1);
|
||||
|
||||
b->name = (char *)benchmark_name; /* XXX strdup? */
|
||||
b->size = size;
|
||||
b->func = benchmark_func;
|
||||
b->data = user_data;
|
||||
}
|
||||
|
||||
static int
|
||||
benchmark_suite_run (BenchmarkSuite *bs)
|
||||
{
|
||||
const guint n_benchmarks = bs->benchmarks->len;
|
||||
const gboolean profile = bs->profile_benchmark_name != NULL;
|
||||
guint i;
|
||||
|
||||
if (profile)
|
||||
{
|
||||
/* For profiling, we only run the selected benchmark. */
|
||||
gboolean found = FALSE;
|
||||
|
||||
|
||||
for (i = 0; i < n_benchmarks; i ++)
|
||||
{
|
||||
Benchmark *b = &g_array_index (bs->benchmarks, Benchmark, i);
|
||||
|
||||
if (strcmp (bs->profile_benchmark_name, b->name) == 0)
|
||||
{
|
||||
b->profile = TRUE;
|
||||
b->func (b, b->size, b->data);
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
g_error ("No benchmark '%s' found", bs->profile_benchmark_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < n_benchmarks; i ++)
|
||||
{
|
||||
gint64 samples[SAMPLE_SIZE];
|
||||
Benchmark *b = &g_array_index (bs->benchmarks, Benchmark, i);
|
||||
int s, x, y;
|
||||
|
||||
for (s = 0; s < SAMPLE_SIZE; s ++)
|
||||
{
|
||||
b->start_time = 0;
|
||||
b->end_time = 0;
|
||||
|
||||
b->func (b, b->size, b->data);
|
||||
|
||||
if (b->start_time == 0)
|
||||
g_error ("Benchmark '%s' did not call benchmark_start()", b->name);
|
||||
|
||||
if (b->end_time == 0)
|
||||
g_error ("Benchmark '%s' did not call benchmark_stop()", b->name);
|
||||
|
||||
samples[s] = b->end_time - b->start_time;
|
||||
}
|
||||
|
||||
/* Bubble sort \o/ */
|
||||
for (x = 0; x < SAMPLE_SIZE; x ++)
|
||||
for (y = 0; y < SAMPLE_SIZE; y ++)
|
||||
if (samples[x] < samples[y])
|
||||
{
|
||||
int k = samples[x];
|
||||
samples[x] = samples[y];
|
||||
samples[y] = k;
|
||||
}
|
||||
|
||||
/* Median of SAMPLE_SIZE */
|
||||
printf ("%s (%" G_GSIZE_FORMAT ") | %.2f\n", b->name, b->size,
|
||||
samples[SAMPLE_SIZE / 2 + 1] / 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
237
benchmarks/containers.c
Normal file
237
benchmarks/containers.c
Normal file
@@ -0,0 +1,237 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "benchmark.h"
|
||||
|
||||
/* Command line options */
|
||||
const char *profile_benchmark_name = NULL;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "profile", 'p', 0, G_OPTION_ARG_STRING, &profile_benchmark_name, "Benchmark name to profile using callgrind", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GType type;
|
||||
} ContainerData;
|
||||
|
||||
/*
|
||||
* PROFILING:
|
||||
*
|
||||
* valgrind --tool=callgrind --instr-atstart=no benchmarks/name --profile="benchmark name"
|
||||
*
|
||||
* ENJOY.
|
||||
*/
|
||||
|
||||
static void
|
||||
container_create_benchmark (Benchmark *b,
|
||||
gsize size,
|
||||
gpointer user_data)
|
||||
{
|
||||
ContainerData *data = user_data;
|
||||
guint i;
|
||||
GtkWidget **widgets = g_malloc (sizeof (GtkWidget*) * size);
|
||||
|
||||
benchmark_start (b);
|
||||
for (i = 0; i < size; i ++)
|
||||
widgets[i] = g_object_new (data->type, NULL);
|
||||
benchmark_stop (b);
|
||||
|
||||
g_free (widgets);
|
||||
}
|
||||
|
||||
static void
|
||||
container_destroy_benchmark (Benchmark *b,
|
||||
gsize size,
|
||||
gpointer user_data)
|
||||
{
|
||||
ContainerData *data = user_data;
|
||||
guint i;
|
||||
GtkWidget **widgets = g_malloc (sizeof (GtkWidget*) * size);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
{
|
||||
widgets[i] = g_object_new (data->type, NULL);
|
||||
g_object_ref_sink (widgets[i]);
|
||||
}
|
||||
|
||||
benchmark_start (b);
|
||||
for (i = 0; i < size; i ++)
|
||||
g_object_unref (widgets[i]);
|
||||
benchmark_stop (b);
|
||||
|
||||
g_free (widgets);
|
||||
}
|
||||
|
||||
static void
|
||||
container_add_benchmark (Benchmark *b,
|
||||
gsize size,
|
||||
gpointer user_data)
|
||||
{
|
||||
ContainerData *data = user_data;
|
||||
guint i;
|
||||
GtkWidget *container;
|
||||
GtkWidget **buttons = g_malloc (sizeof (GtkWidget*) * size);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
buttons[i] = gtk_button_new ();
|
||||
|
||||
container = g_object_new (data->type, NULL);
|
||||
|
||||
benchmark_start (b);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
gtk_container_add ((GtkContainer *)container, buttons[i]);
|
||||
|
||||
benchmark_stop (b);
|
||||
|
||||
g_free (buttons);
|
||||
}
|
||||
|
||||
static void
|
||||
container_remove_benchmark (Benchmark *b,
|
||||
gsize size,
|
||||
gpointer user_data)
|
||||
{
|
||||
ContainerData *data = user_data;
|
||||
guint i;
|
||||
GtkWidget *container;
|
||||
GtkWidget **buttons = g_malloc (sizeof (GtkWidget*) * size);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
{
|
||||
buttons[i] = gtk_button_new ();
|
||||
/* We add an extra ref here so the later remove() does NOT dispose the buttons. */
|
||||
g_object_ref_sink (buttons[i]);
|
||||
g_object_ref (buttons[i]);
|
||||
}
|
||||
|
||||
container = g_object_new (data->type, NULL);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
gtk_container_add ((GtkContainer *)container, buttons[i]);
|
||||
|
||||
benchmark_start (b);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
gtk_container_remove ((GtkContainer *)container, buttons[i]);
|
||||
|
||||
benchmark_stop (b);
|
||||
|
||||
g_free (buttons);
|
||||
}
|
||||
|
||||
static void
|
||||
container_measure_benchmark (Benchmark *b,
|
||||
gsize size,
|
||||
gpointer user_data)
|
||||
{
|
||||
ContainerData *data = user_data;
|
||||
guint i;
|
||||
GtkWidget *container;
|
||||
GtkWidget **buttons = g_malloc (sizeof (GtkWidget*) * size);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
buttons[i] = gtk_button_new ();
|
||||
|
||||
container = g_object_new (data->type, NULL);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
gtk_container_add ((GtkContainer *)container, buttons[i]);
|
||||
|
||||
benchmark_start (b);
|
||||
|
||||
gtk_widget_measure (container, GTK_ORIENTATION_HORIZONTAL, -1,
|
||||
NULL, NULL, NULL, NULL);
|
||||
|
||||
benchmark_stop (b);
|
||||
|
||||
g_free (buttons);
|
||||
}
|
||||
|
||||
static void
|
||||
container_allocate_benchmark (Benchmark *b,
|
||||
gsize size,
|
||||
gpointer user_data)
|
||||
{
|
||||
ContainerData *data = user_data;
|
||||
guint i;
|
||||
GtkWidget *container;
|
||||
GtkWidget **buttons = g_malloc (sizeof (GtkWidget*) * size);
|
||||
int width, height;
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
buttons[i] = gtk_button_new ();
|
||||
|
||||
container = g_object_new (data->type, NULL);
|
||||
|
||||
for (i = 0; i < size; i ++)
|
||||
gtk_container_add ((GtkContainer *)container, buttons[i]);
|
||||
|
||||
|
||||
gtk_widget_measure (container, GTK_ORIENTATION_HORIZONTAL, -1,
|
||||
&width, NULL, NULL, NULL);
|
||||
gtk_widget_measure (container, GTK_ORIENTATION_VERTICAL, width,
|
||||
&height, NULL, NULL, NULL);
|
||||
|
||||
benchmark_start (b);
|
||||
gtk_widget_size_allocate (container,
|
||||
&(GtkAllocation){0, 0, width, height},
|
||||
-1);
|
||||
benchmark_stop (b);
|
||||
|
||||
g_free (buttons);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
BenchmarkSuite suite;
|
||||
GOptionContext *option_context;
|
||||
GError *error = NULL;
|
||||
const GType types[] = {
|
||||
GTK_TYPE_BOX,
|
||||
GTK_TYPE_GRID,
|
||||
GTK_TYPE_STACK,
|
||||
/* GTK_TYPE_NOTEBOOK, XXX too slow! :( */
|
||||
};
|
||||
int i;
|
||||
int N = 10000;
|
||||
|
||||
option_context = g_option_context_new ("");
|
||||
g_option_context_add_main_entries (option_context, options, NULL);
|
||||
if (!g_option_context_parse (option_context, &argc, &argv, &error))
|
||||
{
|
||||
g_printerr ("Option parsing failed: %s\n", error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
benchmark_suite_init (&suite, profile_benchmark_name);
|
||||
gtk_init ();
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (types); i ++)
|
||||
{
|
||||
ContainerData *data = g_malloc (sizeof (ContainerData));
|
||||
data->type = types[i];
|
||||
|
||||
benchmark_suite_add (&suite,
|
||||
g_strdup_printf ("%s create", g_type_name (types[i])),
|
||||
N, container_create_benchmark, data);
|
||||
benchmark_suite_add (&suite,
|
||||
g_strdup_printf ("%s destroy", g_type_name (types[i])),
|
||||
N, container_destroy_benchmark, data);
|
||||
benchmark_suite_add (&suite,
|
||||
g_strdup_printf ("%s add", g_type_name (types[i])),
|
||||
N, container_add_benchmark, data);
|
||||
benchmark_suite_add (&suite,
|
||||
g_strdup_printf ("%s remove", g_type_name (types[i])),
|
||||
N, container_remove_benchmark, data);
|
||||
benchmark_suite_add (&suite,
|
||||
g_strdup_printf ("%s measure", g_type_name (types[i])),
|
||||
N, container_measure_benchmark, data);
|
||||
benchmark_suite_add (&suite,
|
||||
g_strdup_printf ("%s allocate", g_type_name (types[i])),
|
||||
N, container_allocate_benchmark, data);
|
||||
}
|
||||
|
||||
return benchmark_suite_run (&suite);
|
||||
}
|
||||
18
benchmarks/meson.build
Normal file
18
benchmarks/meson.build
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
# benchmark name, optional extra sources
|
||||
gtk_benchmarks = [
|
||||
['containers'],
|
||||
]
|
||||
|
||||
foreach b : gtk_benchmarks
|
||||
b_name = b.get(0)
|
||||
b_sources = ['@0@.c'.format(b_name), b.get(1, [])]
|
||||
b_exec = executable (
|
||||
b_name,
|
||||
b_sources,
|
||||
include_directories: [confinc, gdkinc],
|
||||
dependencies: [libgtk_dep, libm]
|
||||
)
|
||||
benchmark(b_name, b_exec)
|
||||
|
||||
endforeach
|
||||
@@ -645,6 +645,7 @@ subdir('gdk')
|
||||
subdir('gsk')
|
||||
subdir('gtk')
|
||||
subdir('modules')
|
||||
subdir('benchmarks')
|
||||
if get_option('demos')
|
||||
subdir('demos')
|
||||
endif
|
||||
|
||||
Reference in New Issue
Block a user