| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include <ctype.h> | 8 #include <ctype.h> |
| 9 | 9 |
| 10 #include "Benchmark.h" | 10 #include "Benchmark.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 #include "SkSurface.h" | 24 #include "SkSurface.h" |
| 25 | 25 |
| 26 #if SK_SUPPORT_GPU | 26 #if SK_SUPPORT_GPU |
| 27 #include "gl/GrGLDefines.h" | 27 #include "gl/GrGLDefines.h" |
| 28 #include "GrContextFactory.h" | 28 #include "GrContextFactory.h" |
| 29 GrContextFactory gGrFactory; | 29 GrContextFactory gGrFactory; |
| 30 #endif | 30 #endif |
| 31 | 31 |
| 32 __SK_FORCE_IMAGE_DECODER_LINKING; | 32 __SK_FORCE_IMAGE_DECODER_LINKING; |
| 33 | 33 |
| 34 #if SK_DEBUG | 34 static const int kAutoTuneLoops = -1; |
| 35 DEFINE_bool(runOnce, true, "Run each benchmark just once?"); | 35 |
| 36 static const int kDefaultLoops = |
| 37 #ifdef SK_DEBUG |
| 38 1; |
| 36 #else | 39 #else |
| 37 DEFINE_bool(runOnce, false, "Run each benchmark just once?"); | 40 kAutoTuneLoops; |
| 38 #endif | 41 #endif |
| 39 | 42 |
| 43 static SkString loops_help_txt() { |
| 44 SkString help; |
| 45 help.printf("Number of times to run each bench. Set this to %d to auto-" |
| 46 "tune for each bench. Timings are only reported when auto-tuning
.", |
| 47 kAutoTuneLoops); |
| 48 return help; |
| 49 } |
| 50 |
| 51 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str()); |
| 52 |
| 53 DEFINE_string2(writePath, w, "", "If set, write benches here as .pngs."); |
| 54 |
| 40 DEFINE_int32(samples, 10, "Number of samples to measure for each bench."); | 55 DEFINE_int32(samples, 10, "Number of samples to measure for each bench."); |
| 41 DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead."); | 56 DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead."); |
| 42 DEFINE_double(overheadGoal, 0.0001, | 57 DEFINE_double(overheadGoal, 0.0001, |
| 43 "Loop until timer overhead is at most this fraction of our measurm
ents."); | 58 "Loop until timer overhead is at most this fraction of our measurm
ents."); |
| 44 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); | 59 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); |
| 45 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); | 60 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); |
| 46 | 61 |
| 47 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); | 62 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); |
| 48 DEFINE_int32(maxCalibrationAttempts, 3, | 63 DEFINE_int32(maxCalibrationAttempts, 3, |
| 49 "Try up to this many times to guess loops for a bench, or skip the
bench."); | 64 "Try up to this many times to guess loops for a bench, or skip the
bench."); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 60 #ifdef SK_BUILD_FOR_WIN | 75 #ifdef SK_BUILD_FOR_WIN |
| 61 if (ms < 1) return SkStringPrintf("%.3gus", ms*1e3); | 76 if (ms < 1) return SkStringPrintf("%.3gus", ms*1e3); |
| 62 #else | 77 #else |
| 63 if (ms < 1) return SkStringPrintf("%.3gµs", ms*1e3); | 78 if (ms < 1) return SkStringPrintf("%.3gµs", ms*1e3); |
| 64 #endif | 79 #endif |
| 65 return SkStringPrintf("%.3gms", ms); | 80 return SkStringPrintf("%.3gms", ms); |
| 66 } | 81 } |
| 67 #define HUMANIZE(ms) humanize(ms).c_str() | 82 #define HUMANIZE(ms) humanize(ms).c_str() |
| 68 | 83 |
| 69 static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHel
per* gl) { | 84 static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHel
per* gl) { |
| 85 if (canvas) { |
| 86 canvas->clear(SK_ColorWHITE); |
| 87 } |
| 70 WallTimer timer; | 88 WallTimer timer; |
| 71 timer.start(); | 89 timer.start(); |
| 72 if (bench) { | 90 if (bench) { |
| 73 bench->draw(loops, canvas); | 91 bench->draw(loops, canvas); |
| 74 } | 92 } |
| 75 if (canvas) { | 93 if (canvas) { |
| 76 canvas->flush(); | 94 canvas->flush(); |
| 77 } | 95 } |
| 78 #if SK_SUPPORT_GPU | 96 #if SK_SUPPORT_GPU |
| 79 if (gl) { | 97 if (gl) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 98 SkDebugf("ERROR: clamping loops from %d to 1.\n", loops); | 116 SkDebugf("ERROR: clamping loops from %d to 1.\n", loops); |
| 99 return 1; | 117 return 1; |
| 100 } | 118 } |
| 101 if (loops > FLAGS_maxLoops) { | 119 if (loops > FLAGS_maxLoops) { |
| 102 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loo
ps, FLAGS_maxLoops); | 120 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loo
ps, FLAGS_maxLoops); |
| 103 return FLAGS_maxLoops; | 121 return FLAGS_maxLoops; |
| 104 } | 122 } |
| 105 return loops; | 123 return loops; |
| 106 } | 124 } |
| 107 | 125 |
| 126 static bool write_canvas_png(SkCanvas* canvas, const SkString& filename) { |
| 127 if (filename.isEmpty()) { |
| 128 return false; |
| 129 } |
| 130 if (kUnknown_SkColorType == canvas->imageInfo().fColorType) { |
| 131 return false; |
| 132 } |
| 133 SkBitmap bmp; |
| 134 bmp.setInfo(canvas->imageInfo()); |
| 135 if (!canvas->readPixels(&bmp, 0, 0)) { |
| 136 SkDebugf("Can't read canvas pixels.\n"); |
| 137 return false; |
| 138 } |
| 139 SkString dir = SkOSPath::Dirname(filename.c_str()); |
| 140 if (!sk_mkdir(dir.c_str())) { |
| 141 SkDebugf("Can't make dir %s.\n", dir.c_str()); |
| 142 return false; |
| 143 } |
| 144 SkFILEWStream stream(filename.c_str()); |
| 145 if (!stream.isValid()) { |
| 146 SkDebugf("Can't write %s.\n", filename.c_str()); |
| 147 return false; |
| 148 } |
| 149 if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 1
00)) { |
| 150 SkDebugf("Can't encode a PNG.\n"); |
| 151 return false; |
| 152 } |
| 153 return true; |
| 154 } |
| 155 |
| 156 static int kFailedLoops = -2; |
| 108 static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas,
double* samples) { | 157 static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas,
double* samples) { |
| 109 // First figure out approximately how many loops of bench it takes to make o
verhead negligible. | 158 // First figure out approximately how many loops of bench it takes to make o
verhead negligible. |
| 110 double bench_plus_overhead = 0.0; | 159 double bench_plus_overhead = 0.0; |
| 111 int round = 0; | 160 int round = 0; |
| 112 while (bench_plus_overhead < overhead) { | 161 if (kAutoTuneLoops == FLAGS_loops) { |
| 113 if (round++ == FLAGS_maxCalibrationAttempts) { | 162 while (bench_plus_overhead < overhead) { |
| 114 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping
.\n", | 163 if (round++ == FLAGS_maxCalibrationAttempts) { |
| 115 bench->getName(), HUMANIZE(bench_plus_overhead), HUMANIZE(o
verhead)); | 164 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skip
ping.\n", |
| 116 return 0; | 165 bench->getName(), HUMANIZE(bench_plus_overhead), HUMANI
ZE(overhead)); |
| 166 return kFailedLoops; |
| 167 } |
| 168 bench_plus_overhead = time(1, bench, canvas, NULL); |
| 117 } | 169 } |
| 118 bench_plus_overhead = time(1, bench, canvas, NULL); | |
| 119 } | 170 } |
| 120 | 171 |
| 121 // Later we'll just start and stop the timer once but loop N times. | 172 // Later we'll just start and stop the timer once but loop N times. |
| 122 // We'll pick N to make timer overhead negligible: | 173 // We'll pick N to make timer overhead negligible: |
| 123 // | 174 // |
| 124 // overhead | 175 // overhead |
| 125 // ------------------------- < FLAGS_overheadGoal | 176 // ------------------------- < FLAGS_overheadGoal |
| 126 // overhead + N * Bench Time | 177 // overhead + N * Bench Time |
| 127 // | 178 // |
| 128 // where bench_plus_overhead ≈ overhead + Bench Time. | 179 // where bench_plus_overhead ≈ overhead + Bench Time. |
| 129 // | 180 // |
| 130 // Doing some math, we get: | 181 // Doing some math, we get: |
| 131 // | 182 // |
| 132 // (overhead / FLAGS_overheadGoal) - overhead | 183 // (overhead / FLAGS_overheadGoal) - overhead |
| 133 // ------------------------------------------ < N | 184 // ------------------------------------------ < N |
| 134 // bench_plus_overhead - overhead) | 185 // bench_plus_overhead - overhead) |
| 135 // | 186 // |
| 136 // Luckily, this also works well in practice. :) | 187 // Luckily, this also works well in practice. :) |
| 137 const double numer = overhead / FLAGS_overheadGoal - overhead; | 188 int loops = FLAGS_loops; |
| 138 const double denom = bench_plus_overhead - overhead; | 189 if (kAutoTuneLoops == loops) { |
| 139 const int loops = clamp_loops(FLAGS_runOnce ? 1 : (int)ceil(numer / denom)); | 190 const double numer = overhead / FLAGS_overheadGoal - overhead; |
| 191 const double denom = bench_plus_overhead - overhead; |
| 192 loops = (int)ceil(numer / denom); |
| 193 } |
| 194 loops = clamp_loops(loops); |
| 140 | 195 |
| 141 for (int i = 0; i < FLAGS_samples; i++) { | 196 for (int i = 0; i < FLAGS_samples; i++) { |
| 142 samples[i] = time(loops, bench, canvas, NULL) / loops; | 197 samples[i] = time(loops, bench, canvas, NULL) / loops; |
| 143 } | 198 } |
| 144 return loops; | 199 return loops; |
| 145 } | 200 } |
| 146 | 201 |
| 147 #if SK_SUPPORT_GPU | 202 #if SK_SUPPORT_GPU |
| 148 static int gpu_bench(SkGLContextHelper* gl, | 203 static int gpu_bench(SkGLContextHelper* gl, |
| 149 Benchmark* bench, | 204 Benchmark* bench, |
| 150 SkCanvas* canvas, | 205 SkCanvas* canvas, |
| 151 double* samples) { | 206 double* samples) { |
| 152 gl->makeCurrent(); | 207 gl->makeCurrent(); |
| 153 // Make sure we're done with whatever came before. | 208 // Make sure we're done with whatever came before. |
| 154 SK_GL(*gl, Finish()); | 209 SK_GL(*gl, Finish()); |
| 155 | 210 |
| 156 // First, figure out how many loops it'll take to get a frame up to FLAGS_gp
uMs. | 211 // First, figure out how many loops it'll take to get a frame up to FLAGS_gp
uMs. |
| 157 int loops = 1; | 212 int loops = FLAGS_loops; |
| 158 if (!FLAGS_runOnce) { | 213 if (kAutoTuneLoops == loops) { |
| 214 loops = 1; |
| 159 double elapsed = 0; | 215 double elapsed = 0; |
| 160 do { | 216 do { |
| 161 loops *= 2; | 217 loops *= 2; |
| 162 // If the GPU lets frames lag at all, we need to make sure we're tim
ing | 218 // If the GPU lets frames lag at all, we need to make sure we're tim
ing |
| 163 // _this_ round, not still timing last round. We force this by loop
ing | 219 // _this_ round, not still timing last round. We force this by loop
ing |
| 164 // more times than any reasonable GPU will allow frames to lag. | 220 // more times than any reasonable GPU will allow frames to lag. |
| 165 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { | 221 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { |
| 166 elapsed = time(loops, bench, canvas, gl); | 222 elapsed = time(loops, bench, canvas, gl); |
| 167 } | 223 } |
| 168 } while (elapsed < FLAGS_gpuMs); | 224 } while (elapsed < FLAGS_gpuMs); |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 const char* fSourceType; | 523 const char* fSourceType; |
| 468 int fCurrentScale; | 524 int fCurrentScale; |
| 469 int fCurrentSKP; | 525 int fCurrentSKP; |
| 470 }; | 526 }; |
| 471 | 527 |
| 472 int nanobench_main(); | 528 int nanobench_main(); |
| 473 int nanobench_main() { | 529 int nanobench_main() { |
| 474 SetupCrashHandler(); | 530 SetupCrashHandler(); |
| 475 SkAutoGraphics ag; | 531 SkAutoGraphics ag; |
| 476 | 532 |
| 477 if (FLAGS_runOnce) { | 533 if (kAutoTuneLoops != FLAGS_loops) { |
| 478 FLAGS_samples = 1; | 534 FLAGS_samples = 1; |
| 479 FLAGS_gpuFrameLag = 0; | 535 FLAGS_gpuFrameLag = 0; |
| 480 } | 536 } |
| 481 | 537 |
| 538 if (!FLAGS_writePath.isEmpty()) { |
| 539 SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]); |
| 540 if (!sk_mkdir(FLAGS_writePath[0])) { |
| 541 SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_wri
tePath[0]); |
| 542 FLAGS_writePath.set(0, NULL); |
| 543 } |
| 544 } |
| 545 |
| 482 MultiResultsWriter log; | 546 MultiResultsWriter log; |
| 483 SkAutoTDelete<NanoJSONResultsWriter> json; | 547 SkAutoTDelete<NanoJSONResultsWriter> json; |
| 484 if (!FLAGS_outResultsFile.isEmpty()) { | 548 if (!FLAGS_outResultsFile.isEmpty()) { |
| 485 const char* gitHash = FLAGS_gitHash.isEmpty() ? "unknown-revision" : FLA
GS_gitHash[0]; | 549 const char* gitHash = FLAGS_gitHash.isEmpty() ? "unknown-revision" : FLA
GS_gitHash[0]; |
| 486 json.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0], gitHash)
)); | 550 json.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0], gitHash)
)); |
| 487 log.add(json.get()); | 551 log.add(json.get()); |
| 488 } | 552 } |
| 489 CallEnd<MultiResultsWriter> ender(log); | 553 CallEnd<MultiResultsWriter> ender(log); |
| 490 | 554 |
| 491 if (1 == FLAGS_key.count() % 2) { | 555 if (1 == FLAGS_key.count() % 2) { |
| 492 SkDebugf("ERROR: --key must be passed with an even number of arguments.\
n"); | 556 SkDebugf("ERROR: --key must be passed with an even number of arguments.\
n"); |
| 493 return 1; | 557 return 1; |
| 494 } | 558 } |
| 495 for (int i = 1; i < FLAGS_key.count(); i += 2) { | 559 for (int i = 1; i < FLAGS_key.count(); i += 2) { |
| 496 log.key(FLAGS_key[i-1], FLAGS_key[i]); | 560 log.key(FLAGS_key[i-1], FLAGS_key[i]); |
| 497 } | 561 } |
| 498 fill_static_options(&log); | 562 fill_static_options(&log); |
| 499 | 563 |
| 500 const double overhead = estimate_timer_overhead(); | 564 const double overhead = estimate_timer_overhead(); |
| 501 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); | 565 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); |
| 502 | 566 |
| 503 SkAutoTMalloc<double> samples(FLAGS_samples); | 567 SkAutoTMalloc<double> samples(FLAGS_samples); |
| 504 | 568 |
| 505 if (FLAGS_runOnce) { | 569 if (kAutoTuneLoops != FLAGS_loops) { |
| 506 SkDebugf("--runOnce is true; times would only be misleading so we won't
print them.\n"); | 570 SkDebugf("Fixed number of loops; times would only be misleading so we wo
n't print them.\n"); |
| 507 } else if (FLAGS_verbose) { | 571 } else if (FLAGS_verbose) { |
| 508 // No header. | 572 // No header. |
| 509 } else if (FLAGS_quiet) { | 573 } else if (FLAGS_quiet) { |
| 510 SkDebugf("median\tbench\tconfig\n"); | 574 SkDebugf("median\tbench\tconfig\n"); |
| 511 } else { | 575 } else { |
| 512 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\
n"); | 576 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\
n"); |
| 513 } | 577 } |
| 514 | 578 |
| 515 SkTDArray<Config> configs; | 579 SkTDArray<Config> configs; |
| 516 create_configs(&configs); | 580 create_configs(&configs); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 542 #endif | 606 #endif |
| 543 | 607 |
| 544 const int loops = | 608 const int loops = |
| 545 #if SK_SUPPORT_GPU | 609 #if SK_SUPPORT_GPU |
| 546 Benchmark::kGPU_Backend == targets[j]->config.backend | 610 Benchmark::kGPU_Backend == targets[j]->config.backend |
| 547 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get()) | 611 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get()) |
| 548 : | 612 : |
| 549 #endif | 613 #endif |
| 550 cpu_bench( overhead, bench.get(), canvas, samples.get()); | 614 cpu_bench( overhead, bench.get(), canvas, samples.get()); |
| 551 | 615 |
| 552 if (loops == 0) { | 616 if (canvas && !FLAGS_writePath.isEmpty() && NULL != FLAGS_writePath[
0]) { |
| 617 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config
); |
| 618 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getName
()); |
| 619 pngFilename.append(".png"); |
| 620 write_canvas_png(canvas, pngFilename); |
| 621 } |
| 622 |
| 623 if (kFailedLoops == loops) { |
| 553 // Can't be timed. A warning note has already been printed. | 624 // Can't be timed. A warning note has already been printed. |
| 554 continue; | 625 continue; |
| 555 } | 626 } |
| 556 | 627 |
| 557 Stats stats(samples.get(), FLAGS_samples); | 628 Stats stats(samples.get(), FLAGS_samples); |
| 558 log.config(config); | 629 log.config(config); |
| 559 benchStream.fillCurrentOptions(&log); | 630 benchStream.fillCurrentOptions(&log); |
| 560 #if SK_SUPPORT_GPU | 631 #if SK_SUPPORT_GPU |
| 561 if (Benchmark::kGPU_Backend == targets[j]->config.backend) { | 632 if (Benchmark::kGPU_Backend == targets[j]->config.backend) { |
| 562 fill_gpu_options(&log, targets[j]->gl); | 633 fill_gpu_options(&log, targets[j]->gl); |
| 563 } | 634 } |
| 564 #endif | 635 #endif |
| 565 log.timer("min_ms", stats.min); | 636 log.timer("min_ms", stats.min); |
| 566 log.timer("median_ms", stats.median); | 637 log.timer("median_ms", stats.median); |
| 567 log.timer("mean_ms", stats.mean); | 638 log.timer("mean_ms", stats.mean); |
| 568 log.timer("max_ms", stats.max); | 639 log.timer("max_ms", stats.max); |
| 569 log.timer("stddev_ms", sqrt(stats.var)); | 640 log.timer("stddev_ms", sqrt(stats.var)); |
| 570 | 641 |
| 571 if (FLAGS_runOnce) { | 642 if (kAutoTuneLoops != FLAGS_loops) { |
| 572 if (targets.count() == 1) { | 643 if (targets.count() == 1) { |
| 573 config = ""; // Only print the config if we run the same ben
ch on more than one. | 644 config = ""; // Only print the config if we run the same ben
ch on more than one. |
| 574 } | 645 } |
| 575 SkDebugf("%s\t%s\n", bench->getName(), config); | 646 SkDebugf("%s\t%s\n", bench->getName(), config); |
| 576 } else if (FLAGS_verbose) { | 647 } else if (FLAGS_verbose) { |
| 577 for (int i = 0; i < FLAGS_samples; i++) { | 648 for (int i = 0; i < FLAGS_samples; i++) { |
| 578 SkDebugf("%s ", HUMANIZE(samples[i])); | 649 SkDebugf("%s ", HUMANIZE(samples[i])); |
| 579 } | 650 } |
| 580 SkDebugf("%s\n", bench->getName()); | 651 SkDebugf("%s\n", bench->getName()); |
| 581 } else if (FLAGS_quiet) { | 652 } else if (FLAGS_quiet) { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 612 | 683 |
| 613 return 0; | 684 return 0; |
| 614 } | 685 } |
| 615 | 686 |
| 616 #if !defined SK_BUILD_FOR_IOS | 687 #if !defined SK_BUILD_FOR_IOS |
| 617 int main(int argc, char** argv) { | 688 int main(int argc, char** argv) { |
| 618 SkCommandLineFlags::Parse(argc, argv); | 689 SkCommandLineFlags::Parse(argc, argv); |
| 619 return nanobench_main(); | 690 return nanobench_main(); |
| 620 } | 691 } |
| 621 #endif | 692 #endif |
| OLD | NEW |