blob: 445370741665cca3dd64c0c8cd6b41b266fe95b5 [file] [log] [blame]
mtkleinf3723212014-06-25 14:08:00 -07001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
mtkleinbb6a0282014-07-01 08:43:42 -07008#include <ctype.h>
9
mtkleinf3723212014-06-25 14:08:00 -070010#include "Benchmark.h"
11#include "CrashHandler.h"
mtklein60317d0f2014-07-14 11:30:37 -070012#include "ResultsWriter.h"
mtkleinf3723212014-06-25 14:08:00 -070013#include "Stats.h"
14#include "Timer.h"
15
16#include "SkCanvas.h"
17#include "SkCommandLineFlags.h"
18#include "SkForceLinking.h"
19#include "SkGraphics.h"
20#include "SkString.h"
21#include "SkSurface.h"
22
mtkleinbb6a0282014-07-01 08:43:42 -070023#if SK_SUPPORT_GPU
24 #include "GrContextFactory.h"
25 GrContextFactory gGrFactory;
26#endif
27
mtkleinf3723212014-06-25 14:08:00 -070028__SK_FORCE_IMAGE_DECODER_LINKING;
29
mtkleina189ccd2014-07-14 12:28:47 -070030#if SK_DEBUG
31 DEFINE_bool(runOnce, true, "Run each benchmark just once?");
32#else
33 DEFINE_bool(runOnce, false, "Run each benchmark just once?");
34#endif
35
mtkleinf3723212014-06-25 14:08:00 -070036DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
37DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
38DEFINE_double(overheadGoal, 0.0001,
39 "Loop until timer overhead is at most this fraction of our measurments.");
40DEFINE_string(match, "", "The usual filters on file names of benchmarks to measure.");
41DEFINE_bool2(quiet, q, false, "Print only bench name and minimum sample.");
42DEFINE_bool2(verbose, v, false, "Print all samples.");
mtkleinbb6a0282014-07-01 08:43:42 -070043DEFINE_string(config, "nonrendering 8888 gpu", "Configs to measure. Options: "
44 "565 8888 gpu nonrendering debug nullgpu msaa4 msaa16 nvprmsaa4 nvprmsaa16 angle");
45DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
46DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");
mtkleinf3723212014-06-25 14:08:00 -070047
mtklein40b32be2014-07-09 08:46:49 -070048DEFINE_bool(cpu, true, "Master switch for CPU-bound work.");
49DEFINE_bool(gpu, true, "Master switch for GPU-bound work.");
50
mtklein60317d0f2014-07-14 11:30:37 -070051DEFINE_string(outResultsFile, "", "If given, write results here as JSON.");
mtklein1e319f72014-07-15 08:27:06 -070052DEFINE_bool(resetGpuContext, true, "Reset the GrContext before running each bench.");
Mike Kleine3631362014-07-15 17:56:37 -040053DEFINE_int32(maxCalibrationAttempts, 3,
54 "Try up to this many times to guess loops for a bench, or skip the bench.");
mtklein60317d0f2014-07-14 11:30:37 -070055
mtkleinf3723212014-06-25 14:08:00 -070056
57static SkString humanize(double ms) {
58 if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e3);
59 if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
mtklein62386882014-07-15 10:30:31 -070060#ifdef SK_BUILD_FOR_WIN
61 if (ms < 1) return SkStringPrintf("%.3gus", ms*1e3);
62#else
mtkleinf3723212014-06-25 14:08:00 -070063 if (ms < 1) return SkStringPrintf("%.3gµs", ms*1e3);
mtklein62386882014-07-15 10:30:31 -070064#endif
mtkleinf3723212014-06-25 14:08:00 -070065 return SkStringPrintf("%.3gms", ms);
66}
67
mtkleinbb6a0282014-07-01 08:43:42 -070068static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHelper* gl) {
69 WallTimer timer;
70 timer.start();
71 if (bench) {
72 bench->draw(loops, canvas);
73 }
74 if (canvas) {
75 canvas->flush();
76 }
77#if SK_SUPPORT_GPU
78 if (gl) {
79 SK_GL(*gl, Flush());
80 gl->swapBuffers();
81 }
82#endif
83 timer.end();
84 return timer.fWall;
85}
86
mtkleinf3723212014-06-25 14:08:00 -070087static double estimate_timer_overhead() {
88 double overhead = 0;
mtkleinf3723212014-06-25 14:08:00 -070089 for (int i = 0; i < FLAGS_overheadLoops; i++) {
mtkleinbb6a0282014-07-01 08:43:42 -070090 overhead += time(1, NULL, NULL, NULL);
mtkleinf3723212014-06-25 14:08:00 -070091 }
92 return overhead / FLAGS_overheadLoops;
93}
94
mtkleinbb6a0282014-07-01 08:43:42 -070095static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) {
96 // First figure out approximately how many loops of bench it takes to make overhead negligible.
97 double bench_plus_overhead;
Mike Kleine3631362014-07-15 17:56:37 -040098 int round = 0;
mtkleinf3723212014-06-25 14:08:00 -070099 do {
mtkleinbb6a0282014-07-01 08:43:42 -0700100 bench_plus_overhead = time(1, bench, canvas, NULL);
Mike Kleine3631362014-07-15 17:56:37 -0400101 if (++round == FLAGS_maxCalibrationAttempts) {
102 // At some point we have to just give up.
103 return 0;
104 }
105 } while (bench_plus_overhead < overhead);
mtkleinf3723212014-06-25 14:08:00 -0700106
mtkleinbb6a0282014-07-01 08:43:42 -0700107 // Later we'll just start and stop the timer once but loop N times.
mtkleinf3723212014-06-25 14:08:00 -0700108 // We'll pick N to make timer overhead negligible:
109 //
mtkleinbb6a0282014-07-01 08:43:42 -0700110 // overhead
111 // ------------------------- < FLAGS_overheadGoal
112 // overhead + N * Bench Time
mtkleinf3723212014-06-25 14:08:00 -0700113 //
mtkleinbb6a0282014-07-01 08:43:42 -0700114 // where bench_plus_overhead ≈ overhead + Bench Time.
mtkleinf3723212014-06-25 14:08:00 -0700115 //
116 // Doing some math, we get:
117 //
mtkleinbb6a0282014-07-01 08:43:42 -0700118 // (overhead / FLAGS_overheadGoal) - overhead
119 // ------------------------------------------ < N
120 // bench_plus_overhead - overhead)
mtkleinf3723212014-06-25 14:08:00 -0700121 //
122 // Luckily, this also works well in practice. :)
123 const double numer = overhead / FLAGS_overheadGoal - overhead;
mtkleinbb6a0282014-07-01 08:43:42 -0700124 const double denom = bench_plus_overhead - overhead;
mtkleina189ccd2014-07-14 12:28:47 -0700125 const int loops = FLAGS_runOnce ? 1 : (int)ceil(numer / denom);
mtkleinbb6a0282014-07-01 08:43:42 -0700126
127 for (int i = 0; i < FLAGS_samples; i++) {
128 samples[i] = time(loops, bench, canvas, NULL) / loops;
129 }
130 return loops;
mtkleinf3723212014-06-25 14:08:00 -0700131}
132
mtkleinbb6a0282014-07-01 08:43:42 -0700133#if SK_SUPPORT_GPU
134static int gpu_bench(SkGLContextHelper* gl,
135 Benchmark* bench,
136 SkCanvas* canvas,
137 double* samples) {
138 // Make sure we're done with whatever came before.
mtklein9bc86ed2014-07-01 10:02:42 -0700139 SK_GL(*gl, Finish());
mtkleinbb6a0282014-07-01 08:43:42 -0700140
141 // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
142 int loops = 1;
mtkleina189ccd2014-07-14 12:28:47 -0700143 if (!FLAGS_runOnce) {
144 double elapsed = 0;
145 do {
146 loops *= 2;
147 // If the GPU lets frames lag at all, we need to make sure we're timing
148 // _this_ round, not still timing last round. We force this by looping
149 // more times than any reasonable GPU will allow frames to lag.
150 for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
151 elapsed = time(loops, bench, canvas, gl);
152 }
153 } while (elapsed < FLAGS_gpuMs);
mtkleinbb6a0282014-07-01 08:43:42 -0700154
mtkleina189ccd2014-07-14 12:28:47 -0700155 // We've overshot at least a little. Scale back linearly.
156 loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);
mtkleinbb6a0282014-07-01 08:43:42 -0700157
mtkleina189ccd2014-07-14 12:28:47 -0700158 // Might as well make sure we're not still timing our calibration.
159 SK_GL(*gl, Finish());
160 }
mtkleinbb6a0282014-07-01 08:43:42 -0700161
162 // Pretty much the same deal as the calibration: do some warmup to make
163 // sure we're timing steady-state pipelined frames.
164 for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
165 time(loops, bench, canvas, gl);
mtkleinf3723212014-06-25 14:08:00 -0700166 }
mtkleinbb6a0282014-07-01 08:43:42 -0700167
168 // Now, actually do the timing!
169 for (int i = 0; i < FLAGS_samples; i++) {
170 samples[i] = time(loops, bench, canvas, gl) / loops;
171 }
172 return loops;
173}
174#endif
175
176static SkString to_lower(const char* str) {
177 SkString lower(str);
178 for (size_t i = 0; i < lower.size(); i++) {
179 lower[i] = tolower(lower[i]);
180 }
181 return lower;
mtkleinf3723212014-06-25 14:08:00 -0700182}
183
mtkleinbb6a0282014-07-01 08:43:42 -0700184struct Target {
185 const char* config;
186 Benchmark::Backend backend;
187 SkAutoTDelete<SkSurface> surface;
188#if SK_SUPPORT_GPU
189 SkGLContextHelper* gl;
190#endif
191};
mtkleinf3723212014-06-25 14:08:00 -0700192
mtkleinbb6a0282014-07-01 08:43:42 -0700193// If bench is enabled for backend/config, returns a Target* for them, otherwise NULL.
194static Target* is_enabled(Benchmark* bench, Benchmark::Backend backend, const char* config) {
195 if (!bench->isSuitableFor(backend)) {
196 return NULL;
mtkleinf3723212014-06-25 14:08:00 -0700197 }
198
mtkleinbb6a0282014-07-01 08:43:42 -0700199 for (int i = 0; i < FLAGS_config.count(); i++) {
200 if (to_lower(FLAGS_config[i]).equals(config)) {
201 Target* target = new Target;
202 target->config = config;
203 target->backend = backend;
204 return target;
mtkleinf3723212014-06-25 14:08:00 -0700205 }
206 }
mtkleinbb6a0282014-07-01 08:43:42 -0700207 return NULL;
208}
209
210// Append all targets that are suitable for bench.
211static void create_targets(Benchmark* bench, SkTDArray<Target*>* targets) {
212 const int w = bench->getSize().fX,
213 h = bench->getSize().fY;
214 const SkImageInfo _8888 = { w, h, kN32_SkColorType, kPremul_SkAlphaType },
215 _565 = { w, h, kRGB_565_SkColorType, kOpaque_SkAlphaType };
216
217 #define CPU_TARGET(config, backend, code) \
218 if (Target* t = is_enabled(bench, Benchmark::backend, #config)) { \
219 t->surface.reset(code); \
220 targets->push(t); \
221 }
mtklein40b32be2014-07-09 08:46:49 -0700222 if (FLAGS_cpu) {
223 CPU_TARGET(nonrendering, kNonRendering_Backend, NULL)
224 CPU_TARGET(8888, kRaster_Backend, SkSurface::NewRaster(_8888))
225 CPU_TARGET(565, kRaster_Backend, SkSurface::NewRaster(_565))
226 }
mtkleinbb6a0282014-07-01 08:43:42 -0700227
228#if SK_SUPPORT_GPU
mtklein1e319f72014-07-15 08:27:06 -0700229
mtkleinbb6a0282014-07-01 08:43:42 -0700230 #define GPU_TARGET(config, ctxType, info, samples) \
231 if (Target* t = is_enabled(bench, Benchmark::kGPU_Backend, #config)) { \
232 t->surface.reset(SkSurface::NewRenderTarget(gGrFactory.get(ctxType), info, samples)); \
233 t->gl = gGrFactory.getGLContext(ctxType); \
234 targets->push(t); \
235 }
mtklein40b32be2014-07-09 08:46:49 -0700236 if (FLAGS_gpu) {
237 GPU_TARGET(gpu, GrContextFactory::kNative_GLContextType, _8888, 0)
238 GPU_TARGET(msaa4, GrContextFactory::kNative_GLContextType, _8888, 4)
239 GPU_TARGET(msaa16, GrContextFactory::kNative_GLContextType, _8888, 16)
240 GPU_TARGET(nvprmsaa4, GrContextFactory::kNVPR_GLContextType, _8888, 4)
241 GPU_TARGET(nvprmsaa16, GrContextFactory::kNVPR_GLContextType, _8888, 16)
242 GPU_TARGET(debug, GrContextFactory::kDebug_GLContextType, _8888, 0)
243 GPU_TARGET(nullgpu, GrContextFactory::kNull_GLContextType, _8888, 0)
244 #if SK_ANGLE
245 GPU_TARGET(angle, GrContextFactory::kANGLE_GLContextType, _8888, 0)
246 #endif
247 }
mtkleinbb6a0282014-07-01 08:43:42 -0700248#endif
mtkleinf3723212014-06-25 14:08:00 -0700249}
250
mtklein60317d0f2014-07-14 11:30:37 -0700251static void fill_static_options(ResultsWriter* log) {
252#if defined(SK_BUILD_FOR_WIN32)
253 log->option("system", "WIN32");
254#elif defined(SK_BUILD_FOR_MAC)
255 log->option("system", "MAC");
256#elif defined(SK_BUILD_FOR_ANDROID)
257 log->option("system", "ANDROID");
258#elif defined(SK_BUILD_FOR_UNIX)
259 log->option("system", "UNIX");
260#else
261 log->option("system", "other");
262#endif
263#if defined(SK_DEBUG)
264 log->option("build", "DEBUG");
265#else
266 log->option("build", "RELEASE");
267#endif
268}
269
mtkleinf3723212014-06-25 14:08:00 -0700270int tool_main(int argc, char** argv);
271int tool_main(int argc, char** argv) {
272 SetupCrashHandler();
273 SkAutoGraphics ag;
274 SkCommandLineFlags::Parse(argc, argv);
275
mtkleina189ccd2014-07-14 12:28:47 -0700276 if (FLAGS_runOnce) {
277 FLAGS_samples = 1;
278 FLAGS_gpuFrameLag = 0;
279 }
280
mtklein60317d0f2014-07-14 11:30:37 -0700281 MultiResultsWriter log;
282 SkAutoTDelete<JSONResultsWriter> json;
283 if (!FLAGS_outResultsFile.isEmpty()) {
284 json.reset(SkNEW(JSONResultsWriter(FLAGS_outResultsFile[0])));
285 log.add(json.get());
286 }
287 CallEnd<MultiResultsWriter> ender(log);
288 fill_static_options(&log);
289
mtkleinf3723212014-06-25 14:08:00 -0700290 const double overhead = estimate_timer_overhead();
mtkleinbb6a0282014-07-01 08:43:42 -0700291 SkAutoTMalloc<double> samples(FLAGS_samples);
292
mtkleina189ccd2014-07-14 12:28:47 -0700293 if (FLAGS_runOnce) {
294 SkDebugf("--runOnce is true; times would only be misleading so we won't print them.\n");
295 } else if (FLAGS_verbose) {
mtkleinf3723212014-06-25 14:08:00 -0700296 // No header.
297 } else if (FLAGS_quiet) {
mtklein40b32be2014-07-09 08:46:49 -0700298 SkDebugf("median\tbench\tconfig\n");
mtkleinf3723212014-06-25 14:08:00 -0700299 } else {
mtklein5d9d10e2014-07-11 11:57:07 -0700300 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\n");
mtkleinf3723212014-06-25 14:08:00 -0700301 }
302
303 for (const BenchRegistry* r = BenchRegistry::Head(); r != NULL; r = r->next()) {
304 SkAutoTDelete<Benchmark> bench(r->factory()(NULL));
305 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) {
306 continue;
307 }
mtklein60317d0f2014-07-14 11:30:37 -0700308 log.bench(bench->getName(), bench->getSize().fX, bench->getSize().fY);
mtkleinf3723212014-06-25 14:08:00 -0700309
mtkleinbb6a0282014-07-01 08:43:42 -0700310 SkTDArray<Target*> targets;
311 create_targets(bench.get(), &targets);
mtkleinf3723212014-06-25 14:08:00 -0700312
313 bench->preDraw();
mtkleinbb6a0282014-07-01 08:43:42 -0700314 for (int j = 0; j < targets.count(); j++) {
315 SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL;
Mike Kleine3631362014-07-15 17:56:37 -0400316 const char* config = targets[j]->config;
mtkleinf3723212014-06-25 14:08:00 -0700317
mtkleinbb6a0282014-07-01 08:43:42 -0700318 const int loops =
319#if SK_SUPPORT_GPU
320 Benchmark::kGPU_Backend == targets[j]->backend
321 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get())
322 :
323#endif
324 cpu_bench( overhead, bench.get(), canvas, samples.get());
mtkleinf3723212014-06-25 14:08:00 -0700325
Mike Kleine3631362014-07-15 17:56:37 -0400326 if (loops == 0) {
327 SkDebugf("Unable to time %s\t%s (overhead %s)\n",
328 bench->getName(), config, humanize(overhead).c_str());
329 continue;
330 }
331
mtkleinf3723212014-06-25 14:08:00 -0700332 Stats stats(samples.get(), FLAGS_samples);
mtklein60317d0f2014-07-14 11:30:37 -0700333 log.config(config);
334 log.timer("min_ms", stats.min);
335 log.timer("median_ms", stats.median);
336 log.timer("mean_ms", stats.mean);
337 log.timer("max_ms", stats.max);
338 log.timer("stddev_ms", sqrt(stats.var));
339
mtkleina189ccd2014-07-14 12:28:47 -0700340 if (FLAGS_runOnce) {
341 if (targets.count() == 1) {
342 config = ""; // Only print the config if we run the same bench on more than one.
343 }
344 SkDebugf("%s\t%s\n", bench->getName(), config);
345 } else if (FLAGS_verbose) {
mtkleinf3723212014-06-25 14:08:00 -0700346 for (int i = 0; i < FLAGS_samples; i++) {
347 SkDebugf("%s ", humanize(samples[i]).c_str());
348 }
349 SkDebugf("%s\n", bench->getName());
350 } else if (FLAGS_quiet) {
mtkleinbb6a0282014-07-01 08:43:42 -0700351 if (targets.count() == 1) {
mtkleinf3723212014-06-25 14:08:00 -0700352 config = ""; // Only print the config if we run the same bench on more than one.
353 }
mtklein40b32be2014-07-09 08:46:49 -0700354 SkDebugf("%s\t%s\t%s\n", humanize(stats.median).c_str(), bench->getName(), config);
mtkleinf3723212014-06-25 14:08:00 -0700355 } else {
356 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
mtklein5d9d10e2014-07-11 11:57:07 -0700357 SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
mtkleinf3723212014-06-25 14:08:00 -0700358 , loops
359 , humanize(stats.min).c_str()
mtklein40b32be2014-07-09 08:46:49 -0700360 , humanize(stats.median).c_str()
mtkleinf3723212014-06-25 14:08:00 -0700361 , humanize(stats.mean).c_str()
362 , humanize(stats.max).c_str()
363 , stddev_percent
mtklein5d9d10e2014-07-11 11:57:07 -0700364 , stats.plot.c_str()
mtkleinf3723212014-06-25 14:08:00 -0700365 , config
mtkleinbb6a0282014-07-01 08:43:42 -0700366 , bench->getName()
mtkleinf3723212014-06-25 14:08:00 -0700367 );
368 }
369 }
mtkleinbb6a0282014-07-01 08:43:42 -0700370 targets.deleteAll();
Mike Klein3944a1d2014-07-15 13:40:19 -0400371
372 #if SK_SUPPORT_GPU
373 if (FLAGS_resetGpuContext) {
374 gGrFactory.destroyContexts();
375 }
376 #endif
mtkleinf3723212014-06-25 14:08:00 -0700377 }
378
379 return 0;
380}
381
382#if !defined SK_BUILD_FOR_IOS
383int main(int argc, char * const argv[]) {
384 return tool_main(argc, (char**) argv);
385}
386#endif