OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2013 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 | |
8 #include "BenchTimer.h" | |
9 #include "PictureBenchmark.h" | |
10 #include "PictureRenderer.h" | |
11 #include "PictureRenderingFlags.h" | |
12 #include "SkBenchmark.h" | |
13 #include "SkCommandLineFlags.h" | |
14 #include "SkForceLinking.h" | |
15 #include "SkGraphics.h" | |
16 #include "SkStream.h" | |
17 #include "SkString.h" | |
18 #include "TimerData.h" | |
19 | |
20 __SK_FORCE_IMAGE_DECODER_LINKING; | |
21 | |
22 static const int kNumRecordings = SkBENCHLOOP(10); | |
23 static const int kNumPlaybacks = SkBENCHLOOP(5); | |
24 static const size_t kNumBaseBenchmarks = 3; | |
25 static const size_t kNumTileSizes = 3; | |
26 // This is separate from kNumTileSizes because we may add random tile size bench marks later. | |
27 static const size_t kNumBbhPlaybackBenchmarks = 3; | |
28 static const size_t kNumBenchmarks = kNumBaseBenchmarks + kNumBbhPlaybackBenchma rks; | |
29 | |
30 enum BenchmarkType { | |
31 kNormal_BenchmarkType = 0, | |
32 kRTree_BenchmarkType, | |
33 }; | |
34 | |
35 struct Histogram { | |
36 Histogram() { | |
37 // Make fCpuTime negative so that we don't mess with stats: | |
38 fCpuTime = SkIntToScalar(-1); | |
39 } | |
40 SkScalar fCpuTime; | |
41 SkString fPath; | |
42 }; | |
43 | |
44 typedef void (*BenchmarkFunction) | |
45 (BenchmarkType, const SkISize&, const SkString&, SkPicture*, BenchTimer*); | |
46 | |
47 // Defined below. | |
48 static void benchmark_playback( | |
49 BenchmarkType, const SkISize&, const SkString&, SkPicture*, BenchTimer*) ; | |
50 static void benchmark_recording( | |
51 BenchmarkType, const SkISize&, const SkString&, SkPicture*, BenchTimer*) ; | |
52 | |
53 /** | |
54 * Acts as a POD containing information needed to run a benchmark. | |
55 * Provides static methods to poll benchmark info from an index. | |
56 */ | |
57 struct BenchmarkControl { | |
58 SkISize fTileSize; | |
59 BenchmarkType fType; | |
60 BenchmarkFunction fFunction; | |
61 SkString fName; | |
62 | |
63 static BenchmarkControl Make(size_t i) { | |
64 BenchmarkControl benchControl; | |
65 benchControl.fTileSize = getTileSize(i); | |
66 benchControl.fType = getBenchmarkType(i); | |
67 benchControl.fFunction = getBenchmarkFunc(i); | |
68 benchControl.fName = getBenchmarkName(i); | |
69 return benchControl; | |
70 } | |
71 | |
72 enum BaseBenchmarks { | |
73 kNormalRecord = 0, | |
74 kNormalPlayback, | |
75 kRTreeRecord, | |
76 }; | |
77 | |
78 static SkISize fTileSizes[kNumTileSizes]; | |
79 static SkTArray<Histogram> fHistograms[kNumBenchmarks]; | |
80 | |
81 static SkISize getTileSize(size_t i) { | |
82 // Two of the base benchmarks don't need a tile size. But to maintain si mplicity | |
83 // down the pipeline we have to let a couple of values unused. | |
84 if (i < kNumBaseBenchmarks) { | |
85 return SkISize::Make(256, 256); | |
86 } | |
87 if (i >= kNumBaseBenchmarks && i < kNumBenchmarks) { | |
88 return fTileSizes[i - kNumBaseBenchmarks]; | |
89 } | |
90 SkASSERT(0); | |
91 return SkISize::Make(0, 0); | |
92 } | |
93 | |
94 static BenchmarkType getBenchmarkType(size_t i) { | |
95 if (i < kNumBaseBenchmarks) { | |
96 switch (i) { | |
97 case kNormalRecord: | |
98 return kNormal_BenchmarkType; | |
99 case kNormalPlayback: | |
100 return kNormal_BenchmarkType; | |
101 case kRTreeRecord: | |
102 return kRTree_BenchmarkType; | |
103 } | |
104 } | |
105 if (i < kNumBenchmarks) { | |
106 return kRTree_BenchmarkType; | |
107 } else { | |
108 SkASSERT(0); | |
109 } | |
110 return kRTree_BenchmarkType; | |
111 } | |
112 | |
113 static BenchmarkFunction getBenchmarkFunc(size_t i) { | |
114 // Base functions. | |
115 switch (i) { | |
116 case kNormalRecord: | |
117 return benchmark_recording; | |
118 case kNormalPlayback: | |
119 return benchmark_playback; | |
120 case kRTreeRecord: | |
121 return benchmark_recording; | |
122 } | |
123 // RTree playbacks | |
124 if (i < kNumBenchmarks) { | |
125 return benchmark_playback; | |
126 } else { | |
127 SkASSERT(0); | |
128 } | |
129 return NULL; | |
130 } | |
131 | |
132 static SkString getBenchmarkName(size_t i) { | |
133 // Base benchmark names | |
134 switch (i) { | |
135 case kNormalRecord: | |
136 return SkString("normal_recording"); | |
137 case kNormalPlayback: | |
138 return SkString("normal_playback"); | |
139 case kRTreeRecord: | |
140 return SkString("rtree_recording"); | |
141 } | |
142 // RTree benchmark names. | |
143 if (i < kNumBenchmarks) { | |
144 if (i < kNumBaseBenchmarks) { | |
145 SkDebugf("WTF %d", i); | |
146 } | |
sglez
2013/07/13 03:57:15
Woops!!!
This if block was me doing printf debuggi
| |
147 SkASSERT(i >= kNumBaseBenchmarks); | |
148 SkString name; | |
149 name.printf("rtree_playback_%dx%d", | |
150 fTileSizes[i - kNumBaseBenchmarks].fWidth, | |
151 fTileSizes[i - kNumBaseBenchmarks].fHeight); | |
152 return name; | |
153 | |
154 } else { | |
155 SkASSERT(0); | |
156 } | |
157 return SkString(""); | |
158 } | |
159 | |
160 }; | |
161 | |
162 SkISize BenchmarkControl::fTileSizes[kNumTileSizes] = { | |
163 SkISize::Make(256, 256), | |
164 SkISize::Make(512, 512), | |
165 SkISize::Make(1024, 1024), | |
166 }; | |
167 | |
168 // Defined in PictureRenderingFlags.cpp | |
169 extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap ); | |
170 | |
171 static SkPicture* pic_from_path(const char path[]) { | |
172 SkFILEStream stream(path); | |
173 if (!stream.isValid()) { | |
174 SkDebugf("-- Can't open '%s'\n", path); | |
175 return NULL; | |
176 } | |
177 return SkPicture::CreateFromStream(&stream, &lazy_decode_bitmap); | |
178 } | |
179 | |
180 /** | |
181 * This function is the sink to which all work ends up going. | |
182 * Renders the picture into the renderer. It may or may not use an RTree. | |
183 * The renderer is chosen upstream. If we want to measure recording, we will | |
184 * use a RecordPictureRenderer. If we want to measure rendering, we eill use a | |
185 * TiledPictureRenderer. | |
186 */ | |
187 static void do_benchmark_work(sk_tools::PictureRenderer* renderer, | |
188 int benchmarkType, const SkString& path, SkPicture* pic, | |
189 const int numRepeats, const char *msg, BenchTimer* timer) { | |
190 SkString msgPrefix; | |
191 | |
192 switch (benchmarkType){ | |
193 case kNormal_BenchmarkType: | |
194 msgPrefix.set("Normal"); | |
195 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kNone_BBox HierarchyType); | |
196 break; | |
197 case kRTree_BenchmarkType: | |
198 msgPrefix.set("RTree"); | |
199 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kRTree_BBo xHierarchyType); | |
200 break; | |
201 default: | |
202 SkASSERT(0); | |
203 break; | |
204 } | |
205 | |
206 renderer->init(pic); | |
207 | |
208 /** | |
209 * If the renderer is not tiled, assume we are measuring recording. | |
210 */ | |
211 bool isPlayback = (NULL != renderer->getTiledRenderer()); | |
212 | |
213 SkDebugf("%s %s %s %d times...\n", msgPrefix.c_str(), msg, path.c_str(), num Repeats); | |
214 for (int i = 0; i < numRepeats; ++i) { | |
215 renderer->setup(); | |
216 // Render once to fill caches. | |
217 renderer->render(NULL); | |
218 // Render again to measure | |
219 timer->start(); | |
220 bool result = renderer->render(NULL); | |
221 timer->end(); | |
222 // We only care about a false result on playback. RecordPictureRenderer: :render will always | |
223 // return false because we are passing a NULL file name on purpose; whic h is fine. | |
224 if(isPlayback && !result) { | |
225 SkDebugf("Error rendering during playback.\n"); | |
226 } | |
227 } | |
228 renderer->end(); | |
229 } | |
230 | |
231 /** | |
232 * Call do_benchmark_work with a tiled renderer using the default tile dimension s. | |
233 */ | |
234 static void benchmark_playback( | |
235 BenchmarkType benchmarkType, const SkISize& tileSize, | |
236 const SkString& path, SkPicture* pic, BenchTimer* timer) { | |
237 sk_tools::TiledPictureRenderer renderer; | |
238 | |
239 SkString message("tiled_playback"); | |
240 message.appendf("_%dx%d", tileSize.fWidth, tileSize.fHeight); | |
241 do_benchmark_work(&renderer, benchmarkType, | |
242 path, pic, kNumPlaybacks, message.c_str(), timer); | |
243 } | |
244 | |
245 /** | |
246 * Call do_benchmark_work with a RecordPictureRenderer. | |
247 */ | |
248 static void benchmark_recording( | |
249 BenchmarkType benchmarkType, const SkISize& tileSize, | |
250 const SkString& path, SkPicture* pic, BenchTimer* timer) { | |
251 sk_tools::RecordPictureRenderer renderer; | |
252 do_benchmark_work(&renderer, benchmarkType, path, pic, kNumRecordings, "reco rding", timer); | |
253 } | |
254 | |
255 /** | |
256 * Takes argc,argv along with one of the benchmark functions defined above. | |
257 * Will loop along all skp files and perform measurments. | |
258 * | |
259 * Returns a SkScalar representing CPU time taken during benchmark. | |
260 * As a side effect, it spits the timer result to stdout. | |
261 * Will return -1.0 on error. | |
262 */ | |
263 static SkScalar benchmark_loop( | |
264 int argc, | |
265 char **argv, | |
266 const BenchmarkControl& benchControl, | |
267 SkTArray<Histogram>& histogram) { | |
268 | |
269 static const SkString timeFormat("%f"); | |
270 TimerData timerData(timeFormat, timeFormat); | |
271 for (int index = 1; index < argc; ++index) { | |
272 BenchTimer timer; | |
273 SkString path(argv[index]); | |
274 SkAutoTUnref<SkPicture> pic(pic_from_path(path.c_str())); | |
275 if (NULL == pic) { | |
276 SkDebugf("Couldn't create picture. Ignoring path: %s\n", path.c_str( )); | |
277 continue; | |
278 } | |
279 benchControl.fFunction(benchControl.fType, benchControl.fTileSize, path, pic, &timer); | |
280 timerData.appendTimes(&timer, argc - 1 == index); | |
281 | |
282 histogram[index - 1].fPath = path; | |
283 histogram[index - 1].fCpuTime = timer.fCpu; | |
284 } | |
285 | |
286 const SkString timerResult = timerData.getResult( | |
287 /*logPerIter = */ false, | |
288 /*printMin = */ false, | |
289 /*repeatDraw = */ 1, | |
290 /*configName = */ benchControl.fName.c_str(), | |
291 /*showWallTime = */ false, | |
292 /*showTruncatedWallTime = */ false, | |
293 /*showCpuTime = */ true, | |
294 /*showTruncatedCpuTime = */ false, | |
295 /*showGpuTime = */ false); | |
296 | |
297 const char findStr[] = "= "; | |
298 int pos = timerResult.find(findStr); | |
299 if (-1 == pos) { | |
300 SkDebugf("Unexpected output from TimerData::getResult(...). Unable to pa rse."); | |
301 return SkIntToScalar(-1); | |
302 } | |
303 SkDebugf("%s\n", timerResult.c_str()); | |
304 | |
305 SkScalar cpuTime = atof(timerResult.c_str() + pos + sizeof(findStr) - 1); | |
306 if (cpuTime == 0) { // atof returns 0.0 on error. | |
307 SkDebugf("Unable to read value from timer result.\n"); | |
308 return SkIntToScalar(-1); | |
309 } | |
310 return cpuTime; | |
311 } | |
312 | |
313 static int tool_main(int argc, char** argv) { | |
314 SkAutoGraphics ag; | |
315 SkString usage; | |
316 usage.printf("Usage: filename [filename]*\n"); | |
317 | |
318 if (argc < 2) { | |
319 SkDebugf("%s\n", usage.c_str()); | |
320 return -1; | |
321 } | |
322 | |
323 static SkScalar results[kNumBenchmarks]; | |
324 static SkTArray<Histogram> histograms[kNumBenchmarks]; | |
325 | |
326 for (size_t i = 0; i < kNumBenchmarks; ++i) { | |
327 histograms[i] = SkTArray<Histogram>(argc - 1); | |
328 histograms[i].reset(argc - 1); | |
329 results[i] = benchmark_loop( | |
330 argc, argv, | |
331 BenchmarkControl::Make(i), | |
332 histograms[i]); | |
333 } | |
334 | |
335 // Print results | |
336 SkDebugf("\n"); | |
337 for (size_t i = 0; i < kNumBenchmarks; ++i) { | |
338 SkDebugf("%s total: %f\n", BenchmarkControl::getBenchmarkName(i).c_str() , results[i]); | |
339 } | |
340 | |
341 // Print a rough analysis to stdout: | |
342 { | |
343 SkScalar normalRecordResult = results[0]; | |
344 SkScalar normalPlaybackResult = results[1]; | |
345 SkScalar rtreeRecordResult = results[2]; | |
346 SkScalar rtreePlaybackResult = results[3]; | |
347 SkASSERT(normalRecordResult != 0 && normalPlaybackResult != 0); | |
348 SkDebugf("\n"); | |
349 SkDebugf("Recording: Relative difference: %.4f\n", | |
350 rtreeRecordResult / normalRecordResult); | |
351 SkDebugf("Playback (256x256): Relative difference: %.4f\n", | |
352 rtreePlaybackResult / normalPlaybackResult); | |
353 SkScalar times = | |
354 (kNumPlaybacks * (normalRecordResult - rtreeRecordResult)) / | |
355 (kNumRecordings * (rtreePlaybackResult - normalPlaybackResult)); | |
356 SkDebugf("Number of playback repetitions for RTree to be worth it: %d (r atio: %.4f)\n", | |
357 SkScalarCeilToInt(times), times); | |
358 } | |
359 | |
360 for (size_t i = 0; i < kNumBenchmarks; ++i) { | |
361 SkDebugf("%s\n", BenchmarkControl::getBenchmarkName(i).c_str()); | |
362 } | |
363 // Print min/max times for each benchmark. | |
364 SkDebugf("\n"); | |
365 SkScalar minMax[kNumBenchmarks][2]; | |
366 enum { | |
367 kMinBenchmarkTime, | |
368 kMaxBenchmarkTime | |
369 }; | |
370 for (size_t i = 0; i < kNumBenchmarks; ++i) { | |
371 SkString minPath; | |
372 SkString maxPath; | |
373 minMax[i][kMinBenchmarkTime] = SK_ScalarMax; | |
374 minMax[i][kMaxBenchmarkTime] = 0; | |
375 for (int j = 0; j < argc - 1; ++j) { | |
376 SkScalar value = histograms[i][j].fCpuTime; | |
377 if (value < 0) continue; // skp wasn't found or couldn't be read. | |
378 if (value < minMax[i][kMinBenchmarkTime]) { | |
379 minMax[i][kMinBenchmarkTime] = value; | |
380 minPath = histograms[i][j].fPath; | |
381 } | |
382 if (value > minMax[i][kMaxBenchmarkTime]) { | |
383 minMax[i][kMaxBenchmarkTime] = value; | |
384 maxPath = histograms[i][j].fPath; | |
385 } | |
386 } | |
387 SkDebugf("%s min is %.3f:\t\t%s\n" | |
388 "%s max is %.3f:\t\t%s\n", | |
389 BenchmarkControl::getBenchmarkName(i).c_str(), minMax[i][kMinBen chmarkTime], minPath.c_str(), | |
390 BenchmarkControl::getBenchmarkName(i).c_str(), minMax[i][kMaxBen chmarkTime], maxPath.c_str()); | |
391 } | |
392 | |
393 // Output gnuplot readable histogram data.. | |
394 const char* pbTitle = "bbh_shootout_playback.dat"; | |
395 const char* recTitle = "bbh_shootout_record.dat"; | |
396 SkFILEWStream playbackOut(pbTitle); | |
397 SkFILEWStream recordOut(recTitle); | |
398 recordOut.writeText("# Index Normal RTree\n"); | |
399 playbackOut.writeText("# Index Normal RTree\n"); | |
400 for (int i = 0; i < argc - 1; ++i) { | |
401 SkString pbLine; | |
402 SkString recLine; | |
403 // ==== Write record info | |
404 recLine.printf("%d ", i); | |
405 recLine.appendf("%f ", histograms[0][i].fCpuTime); // Append normal_rec ord time | |
406 recLine.appendf("%f", histograms[2][i].fCpuTime); // Append rtree_recor d time | |
407 | |
408 // ==== Write playback info | |
409 pbLine.printf("%d ", i); | |
410 pbLine.appendf("%f ", histograms[1][i].fCpuTime); // Start with normal playback time. | |
411 // Append all playback benchmark times. | |
412 for (size_t j = kNumBbhPlaybackBenchmarks; j < kNumBenchmarks; ++j) { | |
413 pbLine.appendf("%f ", histograms[j][i].fCpuTime); | |
414 } | |
415 pbLine.remove(pbLine.size() - 1, 1); // Remove trailing space from line . | |
416 pbLine.appendf("\n"); | |
417 recLine.appendf("\n"); | |
418 playbackOut.writeText(pbLine.c_str()); | |
419 recordOut.writeText(recLine.c_str()); | |
420 } | |
421 SkDebugf("Wrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle) ; | |
422 | |
423 return 0; | |
424 } | |
425 | |
426 int main(int argc, char** argv) { | |
427 return tool_main(argc, argv); | |
428 } | |
429 | |
OLD | NEW |