Chromium Code Reviews| 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 per-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()); | |
|
mtklein
2014/08/07 18:00:47
This seems somewhat unsafe unless DEFINE_int32 imm
bsalomon
2014/08/07 20:08:10
It does copy it into an SkString.
| |
| 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 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 98 SkDebugf("ERROR: clamping loops from %d to 1.\n", loops); | 113 SkDebugf("ERROR: clamping loops from %d to 1.\n", loops); |
| 99 return 1; | 114 return 1; |
| 100 } | 115 } |
| 101 if (loops > FLAGS_maxLoops) { | 116 if (loops > FLAGS_maxLoops) { |
| 102 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loo ps, FLAGS_maxLoops); | 117 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loo ps, FLAGS_maxLoops); |
| 103 return FLAGS_maxLoops; | 118 return FLAGS_maxLoops; |
| 104 } | 119 } |
| 105 return loops; | 120 return loops; |
| 106 } | 121 } |
| 107 | 122 |
| 123 static bool write_canvas_png(SkCanvas* canvas, const SkString& filename) { | |
| 124 if (filename.isEmpty()) { | |
| 125 return false; | |
| 126 } | |
| 127 if (kUnknown_SkColorType == canvas->imageInfo().fColorType) { | |
| 128 return false; | |
| 129 } | |
| 130 SkBitmap bmp; | |
| 131 bmp.setInfo(canvas->imageInfo()); | |
| 132 if (!canvas->readPixels(&bmp, 0, 0)) { | |
| 133 SkDebugf("Can't read canvas pixels.\n"); | |
| 134 return false; | |
| 135 } | |
| 136 SkString dir = SkOSPath::Dirname(filename.c_str()); | |
| 137 if (!sk_mkdir(dir.c_str())) { | |
| 138 SkDebugf("Can't make dir %s.\n", dir.c_str()); | |
| 139 return false; | |
| 140 } | |
| 141 SkFILEWStream stream(filename.c_str()); | |
| 142 if (!stream.isValid()) { | |
| 143 SkDebugf("Can't write %s.\n", filename.c_str()); | |
| 144 return false; | |
| 145 } | |
| 146 if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 1 00)) { | |
| 147 SkDebugf("Can't encode a PNG.\n"); | |
| 148 return false; | |
| 149 } | |
| 150 return true; | |
| 151 } | |
| 152 | |
| 153 static int kFailedLoops = -2; | |
| 108 static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) { | 154 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. | 155 // First figure out approximately how many loops of bench it takes to make o verhead negligible. |
| 110 double bench_plus_overhead = 0.0; | 156 double bench_plus_overhead = 0.0; |
| 111 int round = 0; | 157 int round = 0; |
| 112 while (bench_plus_overhead < overhead) { | 158 if (kAutoTuneLoops == FLAGS_loops) { |
| 113 if (round++ == FLAGS_maxCalibrationAttempts) { | 159 while (bench_plus_overhead < overhead) { |
| 114 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping .\n", | 160 if (round++ == FLAGS_maxCalibrationAttempts) { |
| 115 bench->getName(), HUMANIZE(bench_plus_overhead), HUMANIZE(o verhead)); | 161 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skip ping.\n", |
| 116 return 0; | 162 bench->getName(), HUMANIZE(bench_plus_overhead), HUMANI ZE(overhead)); |
| 163 return kFailedLoops; | |
| 164 } | |
| 165 bench_plus_overhead = time(1, bench, canvas, NULL); | |
| 117 } | 166 } |
| 118 bench_plus_overhead = time(1, bench, canvas, NULL); | |
| 119 } | 167 } |
| 120 | 168 |
| 121 // Later we'll just start and stop the timer once but loop N times. | 169 // Later we'll just start and stop the timer once but loop N times. |
| 122 // We'll pick N to make timer overhead negligible: | 170 // We'll pick N to make timer overhead negligible: |
| 123 // | 171 // |
| 124 // overhead | 172 // overhead |
| 125 // ------------------------- < FLAGS_overheadGoal | 173 // ------------------------- < FLAGS_overheadGoal |
| 126 // overhead + N * Bench Time | 174 // overhead + N * Bench Time |
| 127 // | 175 // |
| 128 // where bench_plus_overhead ≈ overhead + Bench Time. | 176 // where bench_plus_overhead ≈ overhead + Bench Time. |
| 129 // | 177 // |
| 130 // Doing some math, we get: | 178 // Doing some math, we get: |
| 131 // | 179 // |
| 132 // (overhead / FLAGS_overheadGoal) - overhead | 180 // (overhead / FLAGS_overheadGoal) - overhead |
| 133 // ------------------------------------------ < N | 181 // ------------------------------------------ < N |
| 134 // bench_plus_overhead - overhead) | 182 // bench_plus_overhead - overhead) |
| 135 // | 183 // |
| 136 // Luckily, this also works well in practice. :) | 184 // Luckily, this also works well in practice. :) |
| 137 const double numer = overhead / FLAGS_overheadGoal - overhead; | 185 int loops = FLAGS_loops; |
| 138 const double denom = bench_plus_overhead - overhead; | 186 if (kAutoTuneLoops == loops) { |
| 139 const int loops = clamp_loops(FLAGS_runOnce ? 1 : (int)ceil(numer / denom)); | 187 const double numer = overhead / FLAGS_overheadGoal - overhead; |
| 188 const double denom = bench_plus_overhead - overhead; | |
| 189 loops = (int)ceil(numer / denom); | |
| 190 } | |
| 191 loops = clamp_loops(loops); | |
| 140 | 192 |
| 141 for (int i = 0; i < FLAGS_samples; i++) { | 193 for (int i = 0; i < FLAGS_samples; i++) { |
| 142 samples[i] = time(loops, bench, canvas, NULL) / loops; | 194 samples[i] = time(loops, bench, canvas, NULL) / loops; |
| 143 } | 195 } |
| 144 return loops; | 196 return loops; |
| 145 } | 197 } |
| 146 | 198 |
| 147 #if SK_SUPPORT_GPU | 199 #if SK_SUPPORT_GPU |
| 148 static int gpu_bench(SkGLContextHelper* gl, | 200 static int gpu_bench(SkGLContextHelper* gl, |
| 149 Benchmark* bench, | 201 Benchmark* bench, |
| 150 SkCanvas* canvas, | 202 SkCanvas* canvas, |
| 151 double* samples) { | 203 double* samples) { |
| 152 gl->makeCurrent(); | 204 gl->makeCurrent(); |
| 153 // Make sure we're done with whatever came before. | 205 // Make sure we're done with whatever came before. |
| 154 SK_GL(*gl, Finish()); | 206 SK_GL(*gl, Finish()); |
| 155 | 207 |
| 156 // First, figure out how many loops it'll take to get a frame up to FLAGS_gp uMs. | 208 // First, figure out how many loops it'll take to get a frame up to FLAGS_gp uMs. |
| 157 int loops = 1; | 209 int loops = FLAGS_loops; |
| 158 if (!FLAGS_runOnce) { | 210 if (kAutoTuneLoops == loops) { |
| 211 loops = 1; | |
| 159 double elapsed = 0; | 212 double elapsed = 0; |
| 160 do { | 213 do { |
| 161 loops *= 2; | 214 loops *= 2; |
| 162 // If the GPU lets frames lag at all, we need to make sure we're tim ing | 215 // 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 | 216 // _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. | 217 // more times than any reasonable GPU will allow frames to lag. |
| 165 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { | 218 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { |
| 166 elapsed = time(loops, bench, canvas, gl); | 219 elapsed = time(loops, bench, canvas, gl); |
| 167 } | 220 } |
| 168 } while (elapsed < FLAGS_gpuMs); | 221 } while (elapsed < FLAGS_gpuMs); |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 const char* fSourceType; | 520 const char* fSourceType; |
| 468 int fCurrentScale; | 521 int fCurrentScale; |
| 469 int fCurrentSKP; | 522 int fCurrentSKP; |
| 470 }; | 523 }; |
| 471 | 524 |
| 472 int nanobench_main(); | 525 int nanobench_main(); |
| 473 int nanobench_main() { | 526 int nanobench_main() { |
| 474 SetupCrashHandler(); | 527 SetupCrashHandler(); |
| 475 SkAutoGraphics ag; | 528 SkAutoGraphics ag; |
| 476 | 529 |
| 477 if (FLAGS_runOnce) { | 530 if (kAutoTuneLoops != FLAGS_loops) { |
| 478 FLAGS_samples = 1; | 531 FLAGS_samples = 1; |
| 479 FLAGS_gpuFrameLag = 0; | 532 FLAGS_gpuFrameLag = 0; |
| 480 } | 533 } |
| 481 | 534 |
| 535 if (!FLAGS_writePath.isEmpty()) { | |
| 536 SkDebugf("Writing files to %s.", FLAGS_writePath[0]); | |
| 537 if (FLAGS_writePath.count() > 1) { | |
| 538 SkDebugf(" Ignoring: ", FLAGS_writePath[0]); | |
|
mtklein
2014/08/07 18:00:47
bogus extra arg to SkDebugf?
Maybe it's just me b
bsalomon
2014/08/07 20:08:10
Done.
| |
| 539 for (int i = 1; i < FLAGS_writePath.count(); ++i) { | |
| 540 SkDebugf("%s ", FLAGS_writePath[i]); | |
| 541 } | |
| 542 } | |
| 543 SkDebugf("\n"); | |
| 544 if (!sk_mkdir(FLAGS_writePath[0])) { | |
|
mtklein
2014/08/07 18:00:47
Don't we do this inside write_canvas_png?
bsalomon
2014/08/07 20:08:10
It seems that sk_mkdir doesn't mkdirs recursively.
| |
| 545 SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_wri tePath[0]); | |
| 546 FLAGS_writePath.set(0, NULL); | |
| 547 } | |
| 548 } | |
| 549 | |
| 482 MultiResultsWriter log; | 550 MultiResultsWriter log; |
| 483 SkAutoTDelete<NanoJSONResultsWriter> json; | 551 SkAutoTDelete<NanoJSONResultsWriter> json; |
| 484 if (!FLAGS_outResultsFile.isEmpty()) { | 552 if (!FLAGS_outResultsFile.isEmpty()) { |
| 485 const char* gitHash = FLAGS_gitHash.isEmpty() ? "unknown-revision" : FLA GS_gitHash[0]; | 553 const char* gitHash = FLAGS_gitHash.isEmpty() ? "unknown-revision" : FLA GS_gitHash[0]; |
| 486 json.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0], gitHash) )); | 554 json.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0], gitHash) )); |
| 487 log.add(json.get()); | 555 log.add(json.get()); |
| 488 } | 556 } |
| 489 CallEnd<MultiResultsWriter> ender(log); | 557 CallEnd<MultiResultsWriter> ender(log); |
| 490 | 558 |
| 491 if (1 == FLAGS_key.count() % 2) { | 559 if (1 == FLAGS_key.count() % 2) { |
| 492 SkDebugf("ERROR: --key must be passed with an even number of arguments.\ n"); | 560 SkDebugf("ERROR: --key must be passed with an even number of arguments.\ n"); |
| 493 return 1; | 561 return 1; |
| 494 } | 562 } |
| 495 for (int i = 1; i < FLAGS_key.count(); i += 2) { | 563 for (int i = 1; i < FLAGS_key.count(); i += 2) { |
| 496 log.key(FLAGS_key[i-1], FLAGS_key[i]); | 564 log.key(FLAGS_key[i-1], FLAGS_key[i]); |
| 497 } | 565 } |
| 498 fill_static_options(&log); | 566 fill_static_options(&log); |
| 499 | 567 |
| 500 const double overhead = estimate_timer_overhead(); | 568 const double overhead = estimate_timer_overhead(); |
| 501 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); | 569 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); |
| 502 | 570 |
| 503 SkAutoTMalloc<double> samples(FLAGS_samples); | 571 SkAutoTMalloc<double> samples(FLAGS_samples); |
| 504 | 572 |
| 505 if (FLAGS_runOnce) { | 573 if (kAutoTuneLoops != FLAGS_loops) { |
| 506 SkDebugf("--runOnce is true; times would only be misleading so we won't print them.\n"); | 574 SkDebugf("Fixed number of loops; times would only be misleading so we wo n't print them.\n"); |
| 507 } else if (FLAGS_verbose) { | 575 } else if (FLAGS_verbose) { |
| 508 // No header. | 576 // No header. |
| 509 } else if (FLAGS_quiet) { | 577 } else if (FLAGS_quiet) { |
| 510 SkDebugf("median\tbench\tconfig\n"); | 578 SkDebugf("median\tbench\tconfig\n"); |
| 511 } else { | 579 } else { |
| 512 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\ n"); | 580 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\ n"); |
| 513 } | 581 } |
| 514 | 582 |
| 515 SkTDArray<Config> configs; | 583 SkTDArray<Config> configs; |
| 516 create_configs(&configs); | 584 create_configs(&configs); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 533 SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface-> getCanvas() : NULL; | 601 SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface-> getCanvas() : NULL; |
| 534 const char* config = targets[j]->config.name; | 602 const char* config = targets[j]->config.name; |
| 535 | 603 |
| 536 #if SK_DEBUG | 604 #if SK_DEBUG |
| 537 // skia:2797 Some SKPs SkASSERT in debug mode. Skip them for now. | 605 // skia:2797 Some SKPs SkASSERT in debug mode. Skip them for now. |
| 538 if (0 == strcmp("565", config) && SkStrContains(bench->getName(), ". skp")) { | 606 if (0 == strcmp("565", config) && SkStrContains(bench->getName(), ". skp")) { |
| 539 SkDebugf("Skipping 565 %s. See skia:2797\n", bench->getName()); | 607 SkDebugf("Skipping 565 %s. See skia:2797\n", bench->getName()); |
| 540 continue; | 608 continue; |
| 541 } | 609 } |
| 542 #endif | 610 #endif |
| 543 | 611 SkString pngFilename; |
| 612 if (canvas && !FLAGS_writePath.isEmpty() && NULL != FLAGS_writePath[ 0]) { | |
| 613 canvas->clear(SK_ColorWHITE); | |
|
mtklein
2014/08/07 18:00:47
Perhaps we should always clear canvas to white? a
bsalomon
2014/08/07 20:08:10
Done.
| |
| 614 pngFilename = SkOSPath::Join(FLAGS_writePath[0], config); | |
| 615 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getName ()); | |
| 616 pngFilename.append(".png"); | |
| 617 } | |
| 544 const int loops = | 618 const int loops = |
| 545 #if SK_SUPPORT_GPU | 619 #if SK_SUPPORT_GPU |
| 546 Benchmark::kGPU_Backend == targets[j]->config.backend | 620 Benchmark::kGPU_Backend == targets[j]->config.backend |
| 547 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get()) | 621 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get()) |
| 548 : | 622 : |
| 549 #endif | 623 #endif |
| 550 cpu_bench( overhead, bench.get(), canvas, samples.get()); | 624 cpu_bench( overhead, bench.get(), canvas, samples.get()); |
| 551 | 625 |
| 552 if (loops == 0) { | 626 if (!pngFilename.isEmpty()) { |
| 627 write_canvas_png(canvas, pngFilename); | |
| 628 } | |
| 629 | |
| 630 if (kFailedLoops == loops) { | |
| 553 // Can't be timed. A warning note has already been printed. | 631 // Can't be timed. A warning note has already been printed. |
| 554 continue; | 632 continue; |
| 555 } | 633 } |
| 556 | 634 |
| 557 Stats stats(samples.get(), FLAGS_samples); | 635 Stats stats(samples.get(), FLAGS_samples); |
| 558 log.config(config); | 636 log.config(config); |
| 559 benchStream.fillCurrentOptions(&log); | 637 benchStream.fillCurrentOptions(&log); |
| 560 #if SK_SUPPORT_GPU | 638 #if SK_SUPPORT_GPU |
| 561 if (Benchmark::kGPU_Backend == targets[j]->config.backend) { | 639 if (Benchmark::kGPU_Backend == targets[j]->config.backend) { |
| 562 fill_gpu_options(&log, targets[j]->gl); | 640 fill_gpu_options(&log, targets[j]->gl); |
| 563 } | 641 } |
| 564 #endif | 642 #endif |
| 565 log.timer("min_ms", stats.min); | 643 log.timer("min_ms", stats.min); |
| 566 log.timer("median_ms", stats.median); | 644 log.timer("median_ms", stats.median); |
| 567 log.timer("mean_ms", stats.mean); | 645 log.timer("mean_ms", stats.mean); |
| 568 log.timer("max_ms", stats.max); | 646 log.timer("max_ms", stats.max); |
| 569 log.timer("stddev_ms", sqrt(stats.var)); | 647 log.timer("stddev_ms", sqrt(stats.var)); |
| 570 | 648 |
| 571 if (FLAGS_runOnce) { | 649 if (kAutoTuneLoops != FLAGS_loops) { |
| 572 if (targets.count() == 1) { | 650 if (targets.count() == 1) { |
| 573 config = ""; // Only print the config if we run the same ben ch on more than one. | 651 config = ""; // Only print the config if we run the same ben ch on more than one. |
| 574 } | 652 } |
| 575 SkDebugf("%s\t%s\n", bench->getName(), config); | 653 SkDebugf("%s\t%s\n", bench->getName(), config); |
| 576 } else if (FLAGS_verbose) { | 654 } else if (FLAGS_verbose) { |
| 577 for (int i = 0; i < FLAGS_samples; i++) { | 655 for (int i = 0; i < FLAGS_samples; i++) { |
| 578 SkDebugf("%s ", HUMANIZE(samples[i])); | 656 SkDebugf("%s ", HUMANIZE(samples[i])); |
| 579 } | 657 } |
| 580 SkDebugf("%s\n", bench->getName()); | 658 SkDebugf("%s\n", bench->getName()); |
| 581 } else if (FLAGS_quiet) { | 659 } else if (FLAGS_quiet) { |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 612 | 690 |
| 613 return 0; | 691 return 0; |
| 614 } | 692 } |
| 615 | 693 |
| 616 #if !defined SK_BUILD_FOR_IOS | 694 #if !defined SK_BUILD_FOR_IOS |
| 617 int main(int argc, char** argv) { | 695 int main(int argc, char** argv) { |
| 618 SkCommandLineFlags::Parse(argc, argv); | 696 SkCommandLineFlags::Parse(argc, argv); |
| 619 return nanobench_main(); | 697 return nanobench_main(); |
| 620 } | 698 } |
| 621 #endif | 699 #endif |
| OLD | NEW |