]> Pileus Git - ~andy/gtk/blob - perf/README
stylecontext: Do invalidation on first resize container
[~andy/gtk] / perf / README
1 README for gtk+/perf
2 --------------------
3
4 This is a framework for testing performance in GTK+.   For GTK+, being
5 performant does not only mean "paint widgets fast".  It also means
6 things like the time needed to set up widgets, to map and draw a
7 window for the first time, and emitting/propagating signals.
8
9 The following is accurate as of 2006/Jun/14.
10
11
12 Background
13 ----------
14
15 A widget's lifetime looks more or less like this:
16
17         1. Instantiation
18         2. Size request
19         3. Size allocate
20         5. Realize
21         4. Map
22         5. Expose
23         6. Destroy
24
25 Some of these stages are particularly interesting:
26
27 - Instantiation means creating the widget.  This may be as simple as a
28   few malloc()s and setting some fields.  It could also be a
29   complicated operation if the widget needs to contact an external
30   server to create itself, or if it needs to read data files.
31
32 - Size requisition is when GTK+ asks the widget, "how big do you want
33   to be on the screen"?  This can be an expensive operation.  The
34   widget has to measure its text, measure its icons (and thus load its
35   icons), and generally run through its internal layout code.
36
37 - Realization is when the widget creates its GDK resources, like its
38   GdkWindow and graphics contexts it may need.  This could be
39   expensive if the widget needs to load data files for cursors or
40   backgrounds.
41
42 - Expose is when the widget gets repainted.  This will happen many
43   times throughout the lifetime of the widget:  every time you drag a
44   window on top of it, every time its data changes and it needs to
45   redraw, every time it gets resized.
46
47 GtkWidgetProfiler is a mechanism to let you get individual timings for
48 each of the stages in the lifetime of a widget.  It also lets you run
49 some stages many times in a sequence, so that you can run a real
50 profiler and get an adequate number of samples.  For example,
51 GtkWidgetProfiler lets you say "repaint this widget 1000 times".
52
53 Why is this not as simple as doing
54
55         start_timer ();
56         for (i = 0; i < 1000; i++) {
57                 gtk_widget_queue_draw (widget);
58                 while (gtk_events_pending ())
59                         gtk_main_iteration ();
60         }
61         stop_timer ();
62
63 Huh?
64
65 Because X is an asynchronous window system.  So, when you send the
66 "paint" commands, your program will regain control but it will take
67 some time for the X server to actually process those commands.
68 GtkWidgetProfiler has special code to wait for the X server and give
69 you accurate timings.
70
71
72 Using the framework
73 -------------------
74
75 Right now the framework is very simple; it just has utility functions
76 to time widget creation, mapping, exposure, and destruction.  To run
77 such a test, you use the GtkWidgetProfiler object in
78 gtkwidgetprofiler.h.
79
80 The gtk_widget_profiler_profile_boot() function will emit the
81 "create-widget" signal so that you can create your widget for
82 testing.  It will then take timings for the widget, and emit the
83 "report" signal as appropriate.
84
85 The "create-widget" signal:
86
87   The handler has this form:
88
89     GtkWidget *create_widget_callback (GtkWidgetProfiler *profiler, 
90                                        gpointer user_data);
91
92   You need to create a widget in your handler, and return it.  Do not
93   show the widget; the profiler will do that by itself at the right
94   time, and will actually complain if you show the widget.
95
96
97 The "report" signal:
98
99   This function will get called when the profiler wants to report that
100   it finished timing an important stage in the lifecycle of your
101   widget.  The handler has this form:
102
103     void report_callback (GtkWidgetProfiler      *profiler,
104                           GtkWidgetProfilerReport report,
105                           GtkWidget              *widget,
106                           gdouble                 elapsed,
107                           gpointer                user_data);
108
109   The "report" argument tells you what happened to your widget:
110
111     GTK_WIDGET_PROFILER_REPORT_CREATE.  A timer gets started right
112     before the profiler emits the "create-widget" signal,, and it gets
113     stopped when your callback returns with the new widget.  This
114     measures the time it takes to set up your widget, but not show it.
115
116     GTK_WIDGET_PROFILER_REPORT_MAP.  A timer gets started right before
117     the profiler calls gtk_widget_show_all() on your widget, and it
118     gets stopped when the the widget has been mapped.
119
120     GTK_WIDGET_PROFILER_REPORT_EXPOSE.  A timer gets started right before
121     the profiler starts waiting for GTK+ and the X server to finish
122     painting your widget, and it gets stopped when the widget is fully
123     painted to the screen.
124
125     GTK_WIDGET_PROFILER_REPORT_DESTROY.  A timer gets started right
126     before the profiler calls gtk_widget_destroy() on your widget, and
127     it gets stopped when gtk_widget_destroy() returns.
128
129 As a very basic example of using GtkWidgetProfiler is this:
130
131 ----------------------------------------------------------------------
132 #include <stdio.h>
133 #include <gtk/gtk.h>
134 #include "gtkwidgetprofiler.h"
135
136 static GtkWidget *
137 create_widget_cb (GtkWidgetProfiler *profiler, gpointer data)
138 {
139   GtkWidget *window;
140
141   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
142   /* ... fill the window with widgets, and don't show them ... */
143
144   return window;
145 }
146
147 static void
148 report_cb (GtkWidgetProfiler *profiler, GtkWidgetProfilerReport report, GtkWidget *widget, gdouble elapsed, gpointer data)
149 {
150   const char *type;
151
152   switch (report) {
153   case GTK_WIDGET_PROFILER_REPORT_CREATE:
154     type = "widget creation";
155     break;
156
157   case GTK_WIDGET_PROFILER_REPORT_MAP:
158     type = "widget map";
159     break;
160
161   case GTK_WIDGET_PROFILER_REPORT_EXPOSE:
162     type = "widget expose";
163     break;
164
165   case GTK_WIDGET_PROFILER_REPORT_DESTROY:
166     type = "widget destruction";
167     break;
168
169   default:
170     g_assert_not_reached ();
171     type = NULL;
172   }
173
174   fprintf (stderr, "%s: %g sec\n", type, elapsed);
175
176   if (report == GTK_WIDGET_PROFILER_REPORT_DESTROY)
177     fputs ("\n", stderr);
178 }
179
180 int
181 main (int argc, char **argv)
182 {
183   GtkWidgetProfiler *profiler;
184
185   gtk_init (&argc, &argv);
186
187   profiler = gtk_widget_profiler_new ();
188   g_signal_connect (profiler, "create-widget",
189                     G_CALLBACK (create_widget_cb), NULL);
190   g_signal_connect (profiler, "report",
191                     G_CALLBACK (report_cb), NULL);
192
193   gtk_widget_profiler_set_num_iterations (profiler, 100);
194   gtk_widget_profiler_profile_boot (profiler);
195
196   gtk_widget_profiler_profile_expose (profiler);
197   
198   return 0;
199 }
200
201 ----------------------------------------------------------------------
202
203
204 Getting meaningful results
205 --------------------------
206
207 Getting times for widget creation/mapping/exposing/destruction is
208 interesting, but how do you actually find the places that need
209 optimizing?
210
211 Why, you run the tests under a profiler, of course.
212
213 FIXME: document how to do this.
214
215
216 Feedback
217 --------
218
219 Please mail your feedback to Federico Mena-Quintero <federico@novell.com>.
220 This performance framework is a work in progress.