Add benchmarks

This commit is contained in:
Timm Bäder
2018-04-05 11:02:44 +02:00
parent 0f85831921
commit 3b78ef4170
4 changed files with 412 additions and 0 deletions

156
benchmarks/benchmark.h Normal file
View 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
View 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
View 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

View File

@@ -645,6 +645,7 @@ subdir('gdk')
subdir('gsk')
subdir('gtk')
subdir('modules')
subdir('benchmarks')
if get_option('demos')
subdir('demos')
endif