void cairo_perf_run (cairo_perf_t *perf, const char *name, cairo_perf_func_t perf_func, cairo_count_func_t count_func) { static cairo_bool_t first_run = TRUE; unsigned int i, similar, has_similar; cairo_time_t *times; cairo_stats_t stats = {0.0, 0.0}; int low_std_dev_count; if (perf->list_only) { printf ("%s\n", name); return; } if (first_run) { if (perf->raw) { printf ("[ # ] %s.%-s %s %s %s ...\n", "backend", "content", "test-size", "ticks-per-ms", "time(ticks)"); } if (perf->summary) { fprintf (perf->summary, "[ # ] %8s.%-4s %28s %8s %8s %5s %5s %s %s\n", "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)", "stddev.", "iterations", "overhead"); } first_run = FALSE; } times = perf->times; if (getenv ("CAIRO_PERF_OUTPUT") != NULL) { /* check output */ char *filename; cairo_status_t status; xasprintf (&filename, "%s.%s.%s.%d.out.png", name, perf->target->name, _content_to_string (perf->target->content, 0), perf->size); cairo_save (perf->cr); perf_func (perf->cr, perf->size, perf->size, 1); cairo_restore (perf->cr); status = cairo_surface_write_to_png (cairo_get_target (perf->cr), filename); if (status) { fprintf (stderr, "Failed to generate output check '%s': %s\n", filename, cairo_status_to_string (status)); return; } free (filename); } has_similar = cairo_perf_has_similar (perf); for (similar = 0; similar <= has_similar; similar++) { unsigned loops; if (perf->summary) { fprintf (perf->summary, "[%3d] %8s.%-5s %26s.%-3d ", perf->test_number, perf->target->name, _content_to_string (perf->target->content, similar), name, perf->size); fflush (perf->summary); } /* We run one iteration in advance to warm caches and calibrate. */ cairo_perf_yield (); if (similar) cairo_push_group_with_content (perf->cr, cairo_boilerplate_content (perf->target->content)); else cairo_save (perf->cr); perf_func (perf->cr, perf->size, perf->size, 1); loops = cairo_perf_calibrate (perf, perf_func); if (similar) cairo_pattern_destroy (cairo_pop_group (perf->cr)); else cairo_restore (perf->cr); low_std_dev_count = 0; for (i =0; i < perf->iterations; i++) { cairo_perf_yield (); if (similar) cairo_push_group_with_content (perf->cr, cairo_boilerplate_content (perf->target->content)); else cairo_save (perf->cr); times[i] = perf_func (perf->cr, perf->size, perf->size, loops) ; if (similar) cairo_pattern_destroy (cairo_pop_group (perf->cr)); else cairo_restore (perf->cr); if (perf->raw) { if (i == 0) printf ("[*] %s.%s %s.%d %g", perf->target->name, _content_to_string (perf->target->content, similar), name, perf->size, _cairo_time_to_double (_cairo_time_from_s (1.)) / 1000.); printf (" %lld", (long long) (times[i] / (double) loops)); } else if (! perf->exact_iterations) { if (i > 0) { _cairo_stats_compute (&stats, times, i+1); if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV) { low_std_dev_count++; if (low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT) break; } else { low_std_dev_count = 0; } } } } if (perf->raw) printf ("\n"); if (perf->summary) { _cairo_stats_compute (&stats, times, i); if (count_func != NULL) { double count = count_func (perf->cr, perf->size, perf->size); fprintf (perf->summary, "%.3f [%10lld/%d] %#8.3f %#8.3f %#5.2f%% %3d: %.2f\n", stats.min_ticks /(double) loops, (long long) stats.min_ticks, loops, _cairo_time_to_s (stats.min_ticks) * 1000.0 / loops, _cairo_time_to_s (stats.median_ticks) * 1000.0 / loops, stats.std_dev * 100.0, stats.iterations, count / _cairo_time_to_s (stats.min_ticks)); } else { fprintf (perf->summary, "%.3f [%10lld/%d] %#8.3f %#8.3f %#5.2f%% %3d\n", stats.min_ticks /(double) loops, (long long) stats.min_ticks, loops, _cairo_time_to_s (stats.min_ticks) * 1000.0 / loops, _cairo_time_to_s (stats.median_ticks) * 1000.0 / loops, stats.std_dev * 100.0, stats.iterations); } fflush (perf->summary); } perf->test_number++; } }
static void cairo_perf_trace (cairo_perf_t *perf, const cairo_boilerplate_target_t *target, const char *trace) { static cairo_bool_t first_run = TRUE; unsigned int i; cairo_time_t *times, *paint, *mask, *fill, *stroke, *glyphs; cairo_stats_t stats = {0.0, 0.0}; struct trace args = { target }; int low_std_dev_count; char *trace_cpy, *name; const cairo_script_interpreter_hooks_t hooks = { &args, perf->tile_size ? _tiling_surface_create : _similar_surface_create, NULL, /* surface_destroy */ _context_create, NULL, /* context_destroy */ NULL, /* show_page */ NULL, /* copy_page */ _source_image_create, }; args.tile_size = perf->tile_size; args.observe = perf->observe; trace_cpy = xstrdup (trace); name = basename_no_ext (trace_cpy); if (perf->list_only) { printf ("%s\n", name); free (trace_cpy); return; } if (first_run) { if (perf->raw) { printf ("[ # ] %s.%-s %s %s %s ...\n", "backend", "content", "test-size", "ticks-per-ms", "time(ticks)"); } if (perf->summary) { if (perf->observe) { fprintf (perf->summary, "[ # ] %8s %28s %9s %9s %9s %9s %9s %9s %5s\n", "backend", "test", "total(s)", "paint(s)", "mask(s)", "fill(s)", "stroke(s)", "glyphs(s)", "count"); } else { fprintf (perf->summary, "[ # ] %8s %28s %8s %5s %5s %s\n", "backend", "test", "min(s)", "median(s)", "stddev.", "count"); } } first_run = FALSE; } times = perf->times; paint = times + perf->iterations; mask = paint + perf->iterations; stroke = mask + perf->iterations; fill = stroke + perf->iterations; glyphs = fill + perf->iterations; low_std_dev_count = 0; for (i = 0; i < perf->iterations && ! user_interrupt; i++) { cairo_script_interpreter_t *csi; cairo_status_t status; unsigned int line_no; args.surface = target->create_surface (NULL, CAIRO_CONTENT_COLOR_ALPHA, 1, 1, 1, 1, CAIRO_BOILERPLATE_MODE_PERF, &args.closure); fill_surface(args.surface); /* remove any clear flags */ if (perf->observe) { cairo_surface_t *obs; obs = cairo_surface_create_observer (args.surface, CAIRO_SURFACE_OBSERVER_NORMAL); cairo_surface_destroy (args.surface); args.surface = obs; } if (cairo_surface_status (args.surface)) { fprintf (stderr, "Error: Failed to create target surface: %s\n", target->name); return; } cairo_perf_timer_set_synchronize (target->synchronize, args.closure); if (i == 0) { describe (perf, args.closure); if (perf->summary) { fprintf (perf->summary, "[%3d] %8s %28s ", perf->test_number, perf->target->name, name); fflush (perf->summary); } } csi = cairo_script_interpreter_create (); cairo_script_interpreter_install_hooks (csi, &hooks); if (! perf->observe) { cairo_perf_yield (); cairo_perf_timer_start (); } cairo_script_interpreter_run (csi, trace); line_no = cairo_script_interpreter_get_line_number (csi); /* Finish before querying timings in case we are using an intermediate * target and so need to destroy all surfaces before rendering * commences. */ cairo_script_interpreter_finish (csi); if (perf->observe) { cairo_device_t *observer = cairo_surface_get_device (args.surface); times[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_elapsed (observer)); paint[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_paint_elapsed (observer)); mask[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_mask_elapsed (observer)); stroke[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_stroke_elapsed (observer)); fill[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_fill_elapsed (observer)); glyphs[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_glyphs_elapsed (observer)); } else { fill_surface (args.surface); /* queue a write to the sync'ed surface */ cairo_perf_timer_stop (); times[i] = cairo_perf_timer_elapsed (); } scache_clear (); cairo_surface_destroy (args.surface); if (target->cleanup) target->cleanup (args.closure); status = cairo_script_interpreter_destroy (csi); if (status) { if (perf->summary) { fprintf (perf->summary, "Error during replay, line %d: %s\n", line_no, cairo_status_to_string (status)); } goto out; } if (perf->raw) { if (i == 0) printf ("[*] %s.%s %s.%d %g", perf->target->name, "rgba", name, 0, _cairo_time_to_double (_cairo_time_from_s (1)) / 1000.); printf (" %lld", (long long) times[i]); fflush (stdout); } else if (! perf->exact_iterations) { if (i > CAIRO_PERF_MIN_STD_DEV_COUNT) { _cairo_stats_compute (&stats, times, i+1); if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV) { if (++low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT) break; } else { low_std_dev_count = 0; } } } if (perf->summary && perf->summary_continuous) { _cairo_stats_compute (&stats, times, i+1); fprintf (perf->summary, "\r[%3d] %8s %28s ", perf->test_number, perf->target->name, name); if (perf->observe) { fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, paint, i+1); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, mask, i+1); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, fill, i+1); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, stroke, i+1); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, glyphs, i+1); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); fprintf (perf->summary, " %5d", i+1); } else { fprintf (perf->summary, "%#8.3f %#8.3f %#6.2f%% %4d/%d", _cairo_time_to_s (stats.min_ticks), _cairo_time_to_s (stats.median_ticks), stats.std_dev * 100.0, stats.iterations, i+1); } fflush (perf->summary); } } user_interrupt = 0; if (perf->summary) { _cairo_stats_compute (&stats, times, i); if (perf->summary_continuous) { fprintf (perf->summary, "\r[%3d] %8s %28s ", perf->test_number, perf->target->name, name); } if (perf->observe) { fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, paint, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, mask, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, fill, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, stroke, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); _cairo_stats_compute (&stats, glyphs, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); fprintf (perf->summary, " %5d\n", i); } else { fprintf (perf->summary, "%#8.3f %#8.3f %#6.2f%% %4d/%d\n", _cairo_time_to_s (stats.min_ticks), _cairo_time_to_s (stats.median_ticks), stats.std_dev * 100.0, stats.iterations, i); } fflush (perf->summary); } out: if (perf->raw) { printf ("\n"); fflush (stdout); } perf->test_number++; free (trace_cpy); }