Exemplo n.º 1
0
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);
}