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 |