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 |