| Index: bench/nanobench.cpp
|
| diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
|
| index c808183b194d57d9a1ed5ebd1e12f5b41a2bc5c5..245aa0ed1dda90d9bb845fca28aee230e0b24532 100644
|
| --- a/bench/nanobench.cpp
|
| +++ b/bench/nanobench.cpp
|
| @@ -31,12 +31,27 @@
|
|
|
| __SK_FORCE_IMAGE_DECODER_LINKING;
|
|
|
| -#if SK_DEBUG
|
| - DEFINE_bool(runOnce, true, "Run each benchmark just once?");
|
| +static const int kAutoTuneLoops = -1;
|
| +
|
| +static const int kDefaultLoops =
|
| +#ifdef SK_DEBUG
|
| + 1;
|
| #else
|
| - DEFINE_bool(runOnce, false, "Run each benchmark just once?");
|
| + kAutoTuneLoops;
|
| #endif
|
|
|
| +static SkString loops_help_txt() {
|
| + SkString help;
|
| + help.printf("Number of times to run each bench. Set this to %d to auto-"
|
| + "tune for each bench. Timings are only reported when auto-tuning.",
|
| + kAutoTuneLoops);
|
| + return help;
|
| +}
|
| +
|
| +DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str());
|
| +
|
| +DEFINE_string2(writePath, w, "", "If set, write benches here as .pngs.");
|
| +
|
| DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
|
| DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
|
| DEFINE_double(overheadGoal, 0.0001,
|
| @@ -67,6 +82,9 @@ static SkString humanize(double ms) {
|
| #define HUMANIZE(ms) humanize(ms).c_str()
|
|
|
| static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHelper* gl) {
|
| + if (canvas) {
|
| + canvas->clear(SK_ColorWHITE);
|
| + }
|
| WallTimer timer;
|
| timer.start();
|
| if (bench) {
|
| @@ -105,17 +123,50 @@ static int clamp_loops(int loops) {
|
| return loops;
|
| }
|
|
|
| +static bool write_canvas_png(SkCanvas* canvas, const SkString& filename) {
|
| + if (filename.isEmpty()) {
|
| + return false;
|
| + }
|
| + if (kUnknown_SkColorType == canvas->imageInfo().fColorType) {
|
| + return false;
|
| + }
|
| + SkBitmap bmp;
|
| + bmp.setInfo(canvas->imageInfo());
|
| + if (!canvas->readPixels(&bmp, 0, 0)) {
|
| + SkDebugf("Can't read canvas pixels.\n");
|
| + return false;
|
| + }
|
| + SkString dir = SkOSPath::Dirname(filename.c_str());
|
| + if (!sk_mkdir(dir.c_str())) {
|
| + SkDebugf("Can't make dir %s.\n", dir.c_str());
|
| + return false;
|
| + }
|
| + SkFILEWStream stream(filename.c_str());
|
| + if (!stream.isValid()) {
|
| + SkDebugf("Can't write %s.\n", filename.c_str());
|
| + return false;
|
| + }
|
| + if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 100)) {
|
| + SkDebugf("Can't encode a PNG.\n");
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +static int kFailedLoops = -2;
|
| static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) {
|
| // First figure out approximately how many loops of bench it takes to make overhead negligible.
|
| double bench_plus_overhead = 0.0;
|
| int round = 0;
|
| - while (bench_plus_overhead < overhead) {
|
| - if (round++ == FLAGS_maxCalibrationAttempts) {
|
| - SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n",
|
| - bench->getName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead));
|
| - return 0;
|
| + if (kAutoTuneLoops == FLAGS_loops) {
|
| + while (bench_plus_overhead < overhead) {
|
| + if (round++ == FLAGS_maxCalibrationAttempts) {
|
| + SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n",
|
| + bench->getName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead));
|
| + return kFailedLoops;
|
| + }
|
| + bench_plus_overhead = time(1, bench, canvas, NULL);
|
| }
|
| - bench_plus_overhead = time(1, bench, canvas, NULL);
|
| }
|
|
|
| // Later we'll just start and stop the timer once but loop N times.
|
| @@ -134,9 +185,13 @@ static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas,
|
| // bench_plus_overhead - overhead)
|
| //
|
| // Luckily, this also works well in practice. :)
|
| - const double numer = overhead / FLAGS_overheadGoal - overhead;
|
| - const double denom = bench_plus_overhead - overhead;
|
| - const int loops = clamp_loops(FLAGS_runOnce ? 1 : (int)ceil(numer / denom));
|
| + int loops = FLAGS_loops;
|
| + if (kAutoTuneLoops == loops) {
|
| + const double numer = overhead / FLAGS_overheadGoal - overhead;
|
| + const double denom = bench_plus_overhead - overhead;
|
| + loops = (int)ceil(numer / denom);
|
| + }
|
| + loops = clamp_loops(loops);
|
|
|
| for (int i = 0; i < FLAGS_samples; i++) {
|
| samples[i] = time(loops, bench, canvas, NULL) / loops;
|
| @@ -154,8 +209,9 @@ static int gpu_bench(SkGLContextHelper* gl,
|
| SK_GL(*gl, Finish());
|
|
|
| // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
|
| - int loops = 1;
|
| - if (!FLAGS_runOnce) {
|
| + int loops = FLAGS_loops;
|
| + if (kAutoTuneLoops == loops) {
|
| + loops = 1;
|
| double elapsed = 0;
|
| do {
|
| loops *= 2;
|
| @@ -474,11 +530,19 @@ int nanobench_main() {
|
| SetupCrashHandler();
|
| SkAutoGraphics ag;
|
|
|
| - if (FLAGS_runOnce) {
|
| + if (kAutoTuneLoops != FLAGS_loops) {
|
| FLAGS_samples = 1;
|
| FLAGS_gpuFrameLag = 0;
|
| }
|
|
|
| + if (!FLAGS_writePath.isEmpty()) {
|
| + SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]);
|
| + if (!sk_mkdir(FLAGS_writePath[0])) {
|
| + SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]);
|
| + FLAGS_writePath.set(0, NULL);
|
| + }
|
| + }
|
| +
|
| MultiResultsWriter log;
|
| SkAutoTDelete<NanoJSONResultsWriter> json;
|
| if (!FLAGS_outResultsFile.isEmpty()) {
|
| @@ -502,8 +566,8 @@ int nanobench_main() {
|
|
|
| SkAutoTMalloc<double> samples(FLAGS_samples);
|
|
|
| - if (FLAGS_runOnce) {
|
| - SkDebugf("--runOnce is true; times would only be misleading so we won't print them.\n");
|
| + if (kAutoTuneLoops != FLAGS_loops) {
|
| + SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n");
|
| } else if (FLAGS_verbose) {
|
| // No header.
|
| } else if (FLAGS_quiet) {
|
| @@ -549,7 +613,14 @@ int nanobench_main() {
|
| #endif
|
| cpu_bench( overhead, bench.get(), canvas, samples.get());
|
|
|
| - if (loops == 0) {
|
| + if (canvas && !FLAGS_writePath.isEmpty() && NULL != FLAGS_writePath[0]) {
|
| + SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config);
|
| + pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getName());
|
| + pngFilename.append(".png");
|
| + write_canvas_png(canvas, pngFilename);
|
| + }
|
| +
|
| + if (kFailedLoops == loops) {
|
| // Can't be timed. A warning note has already been printed.
|
| continue;
|
| }
|
| @@ -568,7 +639,7 @@ int nanobench_main() {
|
| log.timer("max_ms", stats.max);
|
| log.timer("stddev_ms", sqrt(stats.var));
|
|
|
| - if (FLAGS_runOnce) {
|
| + if (kAutoTuneLoops != FLAGS_loops) {
|
| if (targets.count() == 1) {
|
| config = ""; // Only print the config if we run the same bench on more than one.
|
| }
|
|
|