profiling: Adds initial UProf support across clutter

UProf is a small library that aims to help applications/libraries provide
domain specific reports about performance.  It currently provides high
precision timer primitives (rdtsc on x86) and simple counters, the ability
to link statistics between optional components at runtime and makes report
generation easy.

This adds initial accounting for:
- Total mainloop time
- Painting
- Picking
- Layouting
- Idle time

The timing done by uprof is of wall clock time. It's not based on stochastic
samples we simply sample a counter at the start and end.  When dealing with
the complexities of GPU drivers and with various kinds of IO this form of
profiling can be quite enlightening as it will be able to represent where
your application is blocking unlike tools such as sysprof.

To enable uprof accounting you must configure Clutter with --enable-profile
and have uprof-0.2 installed from git://git.moblin.org/uprof

If you want to see a report of statistics when Clutter applications exit you
should export CLUTTER_PROFILE_OUTPUT_REPORT=1 before running them.

Just a final word of caution; this stuff is new and the manual nature of
adding uprof instrumentation means it is prone to some errors when modifying
code.  This just means that when you question strange results don't rule out
a mistake in the instrumentation.  Obviously though we hope the benfits out
weigh e.g.  by focusing on very key stats and by having automatic reporting.
This commit is contained in:
Robert Bragg
2009-03-30 16:41:02 +01:00
parent bd41db7c00
commit 0057755854
8 changed files with 376 additions and 2 deletions

170
clutter/clutter-profile.c Normal file
View File

@ -0,0 +1,170 @@
#ifdef CLUTTER_ENABLE_PROFILE
#include "clutter-profile.h"
#include <stdlib.h>
UProfContext *_clutter_uprof_context;
#define REPORT_COLUMN0_WIDTH 40
typedef struct _ClutterUProfReportState
{
gulong n_frames;
} ClutterUProfReportState;
static void
print_counter (UProfCounterResult *counter,
gpointer data)
{
ClutterUProfReportState *state = data;
gulong count = uprof_counter_result_get_count (counter);
if (count == 0)
return;
g_print (" %-*s %-5ld %-5ld\n", REPORT_COLUMN0_WIDTH - 2,
uprof_counter_result_get_name (counter),
uprof_counter_result_get_count (counter),
uprof_counter_result_get_count (counter) / state->n_frames);
}
static char *
print_timer_fields (UProfTimerResult *timer,
guint *fields_width,
gpointer data)
{
ClutterUProfReportState *state = data;
/* Print the field titles when timer == NULL */
if (!timer)
return g_strdup_printf ("Per Frame");
return g_strdup_printf ("%-10.2f",
uprof_timer_result_get_total_msecs (timer) /
(float)state->n_frames);
}
static void
print_report (UProfReport *report, UProfContext *context)
{
GList *root_timers;
GList *l;
UProfTimerResult *stage_paint_timer;
UProfTimerResult *mainloop_timer;
UProfTimerResult *do_pick_timer;
float fps;
ClutterUProfReportState state;
g_print ("\n");
/* FIXME: We need to fix the way Clutter initializes the uprof library
* (we don't currently call uprof_init()) and add a mechanism to know
* if uprof_init hasn't been called so we can simply bail out of report
* generation and not print spurious warning about missing timers.
* Probably we can just have uprof_report_print bail out if uprof wasn't
* initialized, so we don't have to care here.
*/
stage_paint_timer = uprof_context_get_timer_result (context, "Redrawing");
#if 0
if (!stage_paint_timer)
g_critical ("Failed to find \"Redrawing\" timer "
"(you need to update print_report code if you rename it)\n");
#endif
state.n_frames = uprof_timer_result_get_start_count (stage_paint_timer);
g_print ("Frame count = %lu\n", state.n_frames);
mainloop_timer = uprof_context_get_timer_result (context, "Mainloop");
fps = (float)state.n_frames / (uprof_timer_result_get_total_msecs (mainloop_timer)
/ 1000.0);
g_print ("Average fps = %5.2f\n", fps);
do_pick_timer = uprof_context_get_timer_result (context, "Do pick");
if (do_pick_timer)
{
int n_picks = uprof_timer_result_get_start_count (do_pick_timer);
g_print ("Pick Stats:\n");
g_print ("Pick count = %d\n", n_picks);
g_print ("Average picks per frame = %3.2f\n",
(float)n_picks / (float)state.n_frames);
g_print ("Average Msecs per pick = %3.2f\n",
(float)uprof_timer_result_get_total_msecs (do_pick_timer)
/ (float)n_picks);
g_print ("\n");
}
/* XXX: UProfs default reporting code now supports dynamic sizing for the Name
* column, the only thing it's missing is support for adding custom columns but
* when that's added we should switch away from manual report generation. */
g_print ("Counters:\n");
g_print (" %-*s %5s %s\n", REPORT_COLUMN0_WIDTH - 2, "Name", "Total", "Per Frame");
g_print (" %-*s %5s %s\n", REPORT_COLUMN0_WIDTH - 2, "----", "-----", "---------");
uprof_context_foreach_counter (context,
UPROF_COUNTER_SORT_COUNT_INC,
print_counter,
&state);
g_print ("\n");
g_print ("Timers:\n");
root_timers = uprof_context_get_root_timer_results (context);
for (l = root_timers; l != NULL; l = l->next)
uprof_timer_result_print_and_children ((UProfTimerResult *)l->data,
print_timer_fields,
&state);
g_print ("\n");
}
/* FIXME: we should be able to deal with creating the uprof context in
* clutter_init instead. I think the only reason I did it this way originally
* was as a quick hack.
*/
static void __attribute__ ((constructor))
clutter_uprof_constructor (void)
{
_clutter_uprof_context = uprof_context_new ("Clutter");
}
#if 0
static void
print_timers (UProfContext *context)
{
GList *root_timers;
GList *l;
root_timers = uprof_context_get_root_timer_results ();
root_timers =
g_list_sort_with_data (context->root_timers,
(GCompareDataFunc)_uprof_timer_compare_total_times,
NULL);
for (l = context->timers; l != NULL; l = l->next)
{
UProfTimerState *timer = l->data;
timer->children =
g_list_sort_with_data (timer->children,
(GCompareDataFunc)
_uprof_timer_compare_total_times,
NULL);
}
}
#endif
static void __attribute__ ((destructor))
clutter_uprof_destructor (void)
{
if (getenv ("CLUTTER_PROFILE_OUTPUT_REPORT"))
{
UProfReport *report = uprof_report_new ("Clutter report");
uprof_report_add_context (report, _clutter_uprof_context);
uprof_report_add_context_callback (report, print_report);
uprof_report_print (report);
uprof_report_unref (report);
}
uprof_context_unref (_clutter_uprof_context);
}
#endif