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

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: change help text slightly 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 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.");
50 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this."); 65 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this.");
51 DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON."); 66 DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON.");
52 DEFINE_string(gitHash, "", "Git hash to add to JSON."); 67 DEFINE_string(gitHash, "", "Git hash to add to JSON.");
53 68
54 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs."); 69 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs.");
55 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs."); 70 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs.");
56 71
57 static SkString humanize(double ms) { 72 static SkString humanize(double ms) {
58 if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e3); 73 if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e3);
59 if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6); 74 if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
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) {
mtklein 2014/08/07 20:13:11 might put the if (canvas) { canvas->clear(white);
bsalomon 2014/08/07 20:18:07 Done.
70 WallTimer timer; 85 WallTimer timer;
71 timer.start(); 86 timer.start();
72 if (bench) { 87 if (bench) {
73 bench->draw(loops, canvas); 88 bench->draw(loops, canvas);
74 } 89 }
75 if (canvas) { 90 if (canvas) {
76 canvas->flush(); 91 canvas->flush();
77 } 92 }
78 #if SK_SUPPORT_GPU 93 #if SK_SUPPORT_GPU
79 if (gl) { 94 if (gl) {
(...skipping 18 matching lines...) Expand all
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.\n", FLAGS_writePath[0]);
537 if (!sk_mkdir(FLAGS_writePath[0])) {
538 SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_wri tePath[0]);
539 FLAGS_writePath.set(0, NULL);
540 }
541 }
542
482 MultiResultsWriter log; 543 MultiResultsWriter log;
483 SkAutoTDelete<NanoJSONResultsWriter> json; 544 SkAutoTDelete<NanoJSONResultsWriter> json;
484 if (!FLAGS_outResultsFile.isEmpty()) { 545 if (!FLAGS_outResultsFile.isEmpty()) {
485 const char* gitHash = FLAGS_gitHash.isEmpty() ? "unknown-revision" : FLA GS_gitHash[0]; 546 const char* gitHash = FLAGS_gitHash.isEmpty() ? "unknown-revision" : FLA GS_gitHash[0];
486 json.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0], gitHash) )); 547 json.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0], gitHash) ));
487 log.add(json.get()); 548 log.add(json.get());
488 } 549 }
489 CallEnd<MultiResultsWriter> ender(log); 550 CallEnd<MultiResultsWriter> ender(log);
490 551
491 if (1 == FLAGS_key.count() % 2) { 552 if (1 == FLAGS_key.count() % 2) {
492 SkDebugf("ERROR: --key must be passed with an even number of arguments.\ n"); 553 SkDebugf("ERROR: --key must be passed with an even number of arguments.\ n");
493 return 1; 554 return 1;
494 } 555 }
495 for (int i = 1; i < FLAGS_key.count(); i += 2) { 556 for (int i = 1; i < FLAGS_key.count(); i += 2) {
496 log.key(FLAGS_key[i-1], FLAGS_key[i]); 557 log.key(FLAGS_key[i-1], FLAGS_key[i]);
497 } 558 }
498 fill_static_options(&log); 559 fill_static_options(&log);
499 560
500 const double overhead = estimate_timer_overhead(); 561 const double overhead = estimate_timer_overhead();
501 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); 562 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead));
502 563
503 SkAutoTMalloc<double> samples(FLAGS_samples); 564 SkAutoTMalloc<double> samples(FLAGS_samples);
504 565
505 if (FLAGS_runOnce) { 566 if (kAutoTuneLoops != FLAGS_loops) {
506 SkDebugf("--runOnce is true; times would only be misleading so we won't print them.\n"); 567 SkDebugf("Fixed number of loops; times would only be misleading so we wo n't print them.\n");
507 } else if (FLAGS_verbose) { 568 } else if (FLAGS_verbose) {
508 // No header. 569 // No header.
509 } else if (FLAGS_quiet) { 570 } else if (FLAGS_quiet) {
510 SkDebugf("median\tbench\tconfig\n"); 571 SkDebugf("median\tbench\tconfig\n");
511 } else { 572 } else {
512 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\ n"); 573 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\ n");
513 } 574 }
514 575
515 SkTDArray<Config> configs; 576 SkTDArray<Config> configs;
516 create_configs(&configs); 577 create_configs(&configs);
(...skipping 16 matching lines...) Expand all
533 SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface-> getCanvas() : NULL; 594 SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface-> getCanvas() : NULL;
534 const char* config = targets[j]->config.name; 595 const char* config = targets[j]->config.name;
535 596
536 #if SK_DEBUG 597 #if SK_DEBUG
537 // skia:2797 Some SKPs SkASSERT in debug mode. Skip them for now. 598 // skia:2797 Some SKPs SkASSERT in debug mode. Skip them for now.
538 if (0 == strcmp("565", config) && SkStrContains(bench->getName(), ". skp")) { 599 if (0 == strcmp("565", config) && SkStrContains(bench->getName(), ". skp")) {
539 SkDebugf("Skipping 565 %s. See skia:2797\n", bench->getName()); 600 SkDebugf("Skipping 565 %s. See skia:2797\n", bench->getName());
540 continue; 601 continue;
541 } 602 }
542 #endif 603 #endif
604 if (canvas) {
605 canvas->clear(SK_ColorWHITE);
606 }
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
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
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