Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(181)

Side by Side Diff: bench/nanobench.cpp

Issue 450743002: Add option to dump images from nanobench. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: update comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | include/core/SkOSFile.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | include/core/SkOSFile.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698