diff options
author | bsalomon <bsalomon@google.com> | 2014-08-07 14:28:50 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-08-07 14:28:50 -0700 |
commit | 6eb03cc06d0bc60da5277a83aa0251a475794b04 (patch) | |
tree | f359945cb7d54f915bb91f28f44949fffd42e864 | |
parent | 72ebb9f1dc1ed4d64127b02fed93446b278069d1 (diff) |
Add option to dump images from nanobench.
Add option to set the repeat count to any number, replacs the --runOnce flag.
R=mtklein@google.com
Author: bsalomon@google.com
Review URL: https://codereview.chromium.org/450743002
-rw-r--r-- | bench/nanobench.cpp | 109 | ||||
-rw-r--r-- | include/core/SkOSFile.h | 13 | ||||
-rw-r--r-- | src/utils/SkOSFile.cpp | 17 | ||||
-rw-r--r-- | tests/OSPathTest.cpp | 28 |
4 files changed, 142 insertions, 25 deletions
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp index c808183b19..245aa0ed1d 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. } diff --git a/include/core/SkOSFile.h b/include/core/SkOSFile.h index 39eeb4b0b0..69a74dfcaa 100644 --- a/include/core/SkOSFile.h +++ b/include/core/SkOSFile.h @@ -123,7 +123,7 @@ public: /** * Functions for modifying SkStrings which represent paths on the filesystem. */ -class SkOSPath { +class SkOSPath { public: /** * Assembles rootPath and relativePath into a single path, like this: @@ -144,6 +144,17 @@ public: * final slash, or the full name if there is no slash. */ static SkString Basename(const char* fullPath); + + /** + * Given a qualified file name returns the directory. + * Behaves like python's os.path.dirname. If the fullPath is + * /dir/subdir/ the return will be /dir/subdir/ + * @param fullPath Full path to the file. + * @return SkString The dir containing the file - anything preceding the + * final slash, or the full name if ending in a slash. + */ + static SkString Dirname(const char* fullPath); + }; #endif diff --git a/src/utils/SkOSFile.cpp b/src/utils/SkOSFile.cpp index 8baf08ffdd..04a4fe910e 100644 --- a/src/utils/SkOSFile.cpp +++ b/src/utils/SkOSFile.cpp @@ -8,7 +8,7 @@ SkString SkOSPath::Join(const char *rootPath, const char *relativePath) { SkString result(rootPath); - if (!result.endsWith(SkPATH_SEPARATOR)) { + if (!result.endsWith(SkPATH_SEPARATOR) && !result.isEmpty()) { result.appendUnichar(SkPATH_SEPARATOR); } result.append(relativePath); @@ -28,6 +28,21 @@ SkString SkOSPath::Basename(const char* fullPath) { return SkString(filename); } +SkString SkOSPath::Dirname(const char* fullPath) { + if (!fullPath) { + return SkString(); + } + const char* end = strrchr(fullPath, SkPATH_SEPARATOR); + if (NULL == end) { + return SkString(); + } + if (end == fullPath) { + SkASSERT(fullPath[0] == SkPATH_SEPARATOR); + ++end; + } + return SkString(fullPath, end - fullPath); +} + #ifdef SK_BUILD_FOR_WIN static uint16_t* concat_to_16(const char src[], const char suffix[]) diff --git a/tests/OSPathTest.cpp b/tests/OSPathTest.cpp index 1452c3852e..facc6ad3f7 100644 --- a/tests/OSPathTest.cpp +++ b/tests/OSPathTest.cpp @@ -10,7 +10,7 @@ #include "Test.h" /** - * Test SkOSPath::Join and SkOSPath::Basename. + * Test SkOSPath::Join, SkOSPath::Basename, and SkOSPath::Dirname. * Will use SkOSPath::Join to append filename to dir, test that it works correctly, * and tests using SkOSPath::Basename on the result. * @param reporter Reporter for test conditions. @@ -32,16 +32,28 @@ static void test_dir_with_file(skiatest::Reporter* reporter, SkString dir, // fullName should be the combined size of dir and file, plus one if // dir did not include the final path separator. size_t expectedSize = dir.size() + filename.size(); - if (!dir.endsWith(SkPATH_SEPARATOR)) { + if (!dir.endsWith(SkPATH_SEPARATOR) && !dir.isEmpty()) { expectedSize++; } REPORTER_ASSERT(reporter, fullName.size() == expectedSize); SkString basename = SkOSPath::Basename(fullName.c_str()); + SkString dirname = SkOSPath::Dirname(fullName.c_str()); // basename should be the same as filename REPORTER_ASSERT(reporter, basename.equals(filename)); + // dirname should be the same as dir with any trailing seperators removed. + // Except when the the string is just "/". + SkString strippedDir = dir; + while (strippedDir.size() > 2 && strippedDir[strippedDir.size() - 1] == SkPATH_SEPARATOR) { + strippedDir.remove(strippedDir.size() - 1, 1); + } + if (!dirname.equals(strippedDir)) { + SkDebugf("OOUCH %s %s %s\n", dir.c_str(), strippedDir.c_str(), dirname.c_str()); + } + REPORTER_ASSERT(reporter, dirname.equals(strippedDir)); + // basename will not contain a path separator REPORTER_ASSERT(reporter, !basename.contains(SkPATH_SEPARATOR)); @@ -78,8 +90,16 @@ DEF_TEST(OSPath, reporter) { SkString empty = SkOSPath::Basename(NULL); REPORTER_ASSERT(reporter, empty.size() == 0); + // File in root dir + dir.printf("%c", SkPATH_SEPARATOR); + filename.set("file"); + test_dir_with_file(reporter, dir, filename); + + // Just the root dir + filename.reset(); + test_dir_with_file(reporter, dir, filename); + // Test that NULL can be used for the directory and filename. SkString emptyPath = SkOSPath::Join(NULL, NULL); - REPORTER_ASSERT(reporter, emptyPath.size() == 1); - REPORTER_ASSERT(reporter, emptyPath.contains(SkPATH_SEPARATOR)); + REPORTER_ASSERT(reporter, emptyPath.isEmpty()); } |