Blame perf/README

Packit 98cdb6
README for gtk+/perf
Packit 98cdb6
--------------------
Packit 98cdb6
Packit 98cdb6
This is a framework for testing performance in GTK+.   For GTK+, being
Packit 98cdb6
performant does not only mean "paint widgets fast".  It also means
Packit 98cdb6
things like the time needed to set up widgets, to map and draw a
Packit 98cdb6
window for the first time, and emitting/propagating signals.
Packit 98cdb6
Packit 98cdb6
The following is accurate as of 2006/Jun/14.
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Background
Packit 98cdb6
----------
Packit 98cdb6
Packit 98cdb6
A widget's lifetime looks more or less like this:
Packit 98cdb6
Packit 98cdb6
	1. Instantiation
Packit 98cdb6
	2. Size request
Packit 98cdb6
	3. Size allocate
Packit 98cdb6
	5. Realize
Packit 98cdb6
	4. Map
Packit 98cdb6
	5. Expose
Packit 98cdb6
	6. Destroy
Packit 98cdb6
Packit 98cdb6
Some of these stages are particularly interesting:
Packit 98cdb6
Packit 98cdb6
- Instantiation means creating the widget.  This may be as simple as a
Packit 98cdb6
  few malloc()s and setting some fields.  It could also be a
Packit 98cdb6
  complicated operation if the widget needs to contact an external
Packit 98cdb6
  server to create itself, or if it needs to read data files.
Packit 98cdb6
Packit 98cdb6
- Size requisition is when GTK+ asks the widget, "how big do you want
Packit 98cdb6
  to be on the screen"?  This can be an expensive operation.  The
Packit 98cdb6
  widget has to measure its text, measure its icons (and thus load its
Packit 98cdb6
  icons), and generally run through its internal layout code.
Packit 98cdb6
Packit 98cdb6
- Realization is when the widget creates its GDK resources, like its
Packit 98cdb6
  GdkWindow and graphics contexts it may need.  This could be
Packit 98cdb6
  expensive if the widget needs to load data files for cursors or
Packit 98cdb6
  backgrounds.
Packit 98cdb6
Packit 98cdb6
- Expose is when the widget gets repainted.  This will happen many
Packit 98cdb6
  times throughout the lifetime of the widget:  every time you drag a
Packit 98cdb6
  window on top of it, every time its data changes and it needs to
Packit 98cdb6
  redraw, every time it gets resized.
Packit 98cdb6
Packit 98cdb6
GtkWidgetProfiler is a mechanism to let you get individual timings for
Packit 98cdb6
each of the stages in the lifetime of a widget.  It also lets you run
Packit 98cdb6
some stages many times in a sequence, so that you can run a real
Packit 98cdb6
profiler and get an adequate number of samples.  For example,
Packit 98cdb6
GtkWidgetProfiler lets you say "repaint this widget 1000 times".
Packit 98cdb6
Packit 98cdb6
Why is this not as simple as doing
Packit 98cdb6
Packit 98cdb6
	start_timer ();
Packit 98cdb6
	for (i = 0; i < 1000; i++) {
Packit 98cdb6
		gtk_widget_queue_draw (widget);
Packit 98cdb6
		while (gtk_events_pending ())
Packit 98cdb6
			gtk_main_iteration ();
Packit 98cdb6
	}
Packit 98cdb6
	stop_timer ();
Packit 98cdb6
Packit 98cdb6
Huh?
Packit 98cdb6
Packit 98cdb6
Because X is an asynchronous window system.  So, when you send the
Packit 98cdb6
"paint" commands, your program will regain control but it will take
Packit 98cdb6
some time for the X server to actually process those commands.
Packit 98cdb6
GtkWidgetProfiler has special code to wait for the X server and give
Packit 98cdb6
you accurate timings.
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Using the framework
Packit 98cdb6
-------------------
Packit 98cdb6
Packit 98cdb6
Right now the framework is very simple; it just has utility functions
Packit 98cdb6
to time widget creation, mapping, exposure, and destruction.  To run
Packit 98cdb6
such a test, you use the GtkWidgetProfiler object in
Packit 98cdb6
gtkwidgetprofiler.h.
Packit 98cdb6
Packit 98cdb6
The gtk_widget_profiler_profile_boot() function will emit the
Packit 98cdb6
"create-widget" signal so that you can create your widget for
Packit 98cdb6
testing.  It will then take timings for the widget, and emit the
Packit 98cdb6
"report" signal as appropriate.
Packit 98cdb6
Packit 98cdb6
The "create-widget" signal:
Packit 98cdb6
Packit 98cdb6
  The handler has this form:
Packit 98cdb6
Packit 98cdb6
    GtkWidget *create_widget_callback (GtkWidgetProfiler *profiler, 
Packit 98cdb6
				       gpointer user_data);
Packit 98cdb6
Packit 98cdb6
  You need to create a widget in your handler, and return it.  Do not
Packit 98cdb6
  show the widget; the profiler will do that by itself at the right
Packit 98cdb6
  time, and will actually complain if you show the widget.
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
The "report" signal:
Packit 98cdb6
Packit 98cdb6
  This function will get called when the profiler wants to report that
Packit 98cdb6
  it finished timing an important stage in the lifecycle of your
Packit 98cdb6
  widget.  The handler has this form:
Packit 98cdb6
Packit 98cdb6
    void report_callback (GtkWidgetProfiler      *profiler,
Packit 98cdb6
			  GtkWidgetProfilerReport report,
Packit 98cdb6
			  GtkWidget              *widget,
Packit 98cdb6
			  gdouble                 elapsed,
Packit 98cdb6
			  gpointer                user_data);
Packit 98cdb6
Packit 98cdb6
  The "report" argument tells you what happened to your widget:
Packit 98cdb6
Packit 98cdb6
    GTK_WIDGET_PROFILER_REPORT_CREATE.  A timer gets started right
Packit 98cdb6
    before the profiler emits the "create-widget" signal,, and it gets
Packit 98cdb6
    stopped when your callback returns with the new widget.  This
Packit 98cdb6
    measures the time it takes to set up your widget, but not show it.
Packit 98cdb6
Packit 98cdb6
    GTK_WIDGET_PROFILER_REPORT_MAP.  A timer gets started right before
Packit 98cdb6
    the profiler calls gtk_widget_show_all() on your widget, and it
Packit 98cdb6
    gets stopped when the the widget has been mapped.
Packit 98cdb6
Packit 98cdb6
    GTK_WIDGET_PROFILER_REPORT_EXPOSE.  A timer gets started right before
Packit 98cdb6
    the profiler starts waiting for GTK+ and the X server to finish
Packit 98cdb6
    painting your widget, and it gets stopped when the widget is fully
Packit 98cdb6
    painted to the screen.
Packit 98cdb6
Packit 98cdb6
    GTK_WIDGET_PROFILER_REPORT_DESTROY.  A timer gets started right
Packit 98cdb6
    before the profiler calls gtk_widget_destroy() on your widget, and
Packit 98cdb6
    it gets stopped when gtk_widget_destroy() returns.
Packit 98cdb6
Packit 98cdb6
As a very basic example of using GtkWidgetProfiler is this:
Packit 98cdb6
Packit 98cdb6
----------------------------------------------------------------------
Packit 98cdb6
#include <stdio.h>
Packit 98cdb6
#include <gtk/gtk.h>
Packit 98cdb6
#include "gtkwidgetprofiler.h"
Packit 98cdb6
Packit 98cdb6
static GtkWidget *
Packit 98cdb6
create_widget_cb (GtkWidgetProfiler *profiler, gpointer data)
Packit 98cdb6
{
Packit 98cdb6
  GtkWidget *window;
Packit 98cdb6
Packit 98cdb6
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
Packit 98cdb6
  /* ... fill the window with widgets, and don't show them ... */
Packit 98cdb6
Packit 98cdb6
  return window;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static void
Packit 98cdb6
report_cb (GtkWidgetProfiler *profiler, GtkWidgetProfilerReport report, GtkWidget *widget, gdouble elapsed, gpointer data)
Packit 98cdb6
{
Packit 98cdb6
  const char *type;
Packit 98cdb6
Packit 98cdb6
  switch (report) {
Packit 98cdb6
  case GTK_WIDGET_PROFILER_REPORT_CREATE:
Packit 98cdb6
    type = "widget creation";
Packit 98cdb6
    break;
Packit 98cdb6
Packit 98cdb6
  case GTK_WIDGET_PROFILER_REPORT_MAP:
Packit 98cdb6
    type = "widget map";
Packit 98cdb6
    break;
Packit 98cdb6
Packit 98cdb6
  case GTK_WIDGET_PROFILER_REPORT_EXPOSE:
Packit 98cdb6
    type = "widget expose";
Packit 98cdb6
    break;
Packit 98cdb6
Packit 98cdb6
  case GTK_WIDGET_PROFILER_REPORT_DESTROY:
Packit 98cdb6
    type = "widget destruction";
Packit 98cdb6
    break;
Packit 98cdb6
Packit 98cdb6
  default:
Packit 98cdb6
    g_assert_not_reached ();
Packit 98cdb6
    type = NULL;
Packit 98cdb6
  }
Packit 98cdb6
Packit 98cdb6
  fprintf (stderr, "%s: %g sec\n", type, elapsed);
Packit 98cdb6
Packit 98cdb6
  if (report == GTK_WIDGET_PROFILER_REPORT_DESTROY)
Packit 98cdb6
    fputs ("\n", stderr);
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
int
Packit 98cdb6
main (int argc, char **argv)
Packit 98cdb6
{
Packit 98cdb6
  GtkWidgetProfiler *profiler;
Packit 98cdb6
Packit 98cdb6
  gtk_init (&argc, &argv);
Packit 98cdb6
Packit 98cdb6
  profiler = gtk_widget_profiler_new ();
Packit 98cdb6
  g_signal_connect (profiler, "create-widget",
Packit 98cdb6
		    G_CALLBACK (create_widget_cb), NULL);
Packit 98cdb6
  g_signal_connect (profiler, "report",
Packit 98cdb6
		    G_CALLBACK (report_cb), NULL);
Packit 98cdb6
Packit 98cdb6
  gtk_widget_profiler_set_num_iterations (profiler, 100);
Packit 98cdb6
  gtk_widget_profiler_profile_boot (profiler);
Packit 98cdb6
Packit 98cdb6
  gtk_widget_profiler_profile_expose (profiler);
Packit 98cdb6
  
Packit 98cdb6
  return 0;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
----------------------------------------------------------------------
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Getting meaningful results
Packit 98cdb6
--------------------------
Packit 98cdb6
Packit 98cdb6
Getting times for widget creation/mapping/exposing/destruction is
Packit 98cdb6
interesting, but how do you actually find the places that need
Packit 98cdb6
optimizing?
Packit 98cdb6
Packit 98cdb6
Why, you run the tests under a profiler, of course.
Packit 98cdb6
Packit 98cdb6
FIXME: document how to do this.
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Feedback
Packit 98cdb6
--------
Packit 98cdb6
Packit 98cdb6
Please mail your feedback to Federico Mena-Quintero <federico@novell.com>.
Packit 98cdb6
This performance framework is a work in progress.