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

Side by Side Diff: tools/bbh_shootout.cpp

Issue 16948011: Measure tiled rendering. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Applied suggestions from talk. Avoid IO operations. Support for different tile sizes Created 7 years, 5 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 | Annotate | Revision Log
« no previous file with comments | « gyp/tools.gyp ('k') | tools/lua/bbh_filter.lua » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 "SkCommandLineFlags.h"
13 #include "SkBenchmark.h"
14 #include "SkForceLinking.h"
15 #include "SkStream.h"
16 #include "SkString.h"
17 #include "SkGraphics.h"
caryclark 2013/07/10 15:28:24 as before, alphabetize
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 int kNumTileSizes = 3;
25
26 enum BenchmarkType {
27 kNormal_BenchmarkType = 0,
28 kRTree_BenchmarkType,
29 };
30
31 struct Histogram {
32 int pathIndex;
33 SkScalar cpuTime;
34 };
35
36 // Defined in PictureRenderingFlags.cpp
37 extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap );
38
39 static SkPicture* pic_from_path(const char path[]) {
40 SkFILEStream stream(path);
41 if (!stream.isValid()) {
42 SkDebugf("-- Can't open '%s'\n", path);
43 return NULL;
44 }
45 return SkPicture::CreateFromStream(&stream, &lazy_decode_bitmap);
46 }
47
48 /**
49 * This function is the sink to which all work ends up going.
50 * Renders the picture into the renderer. It may or may not use an RTree.
51 * The renderer is chosen upstream. If we want to measure recording, we will
52 * use a RecordPictureRenderer. If we want to measure rendering, we eill use a
53 * TiledPictureRenderer.
54 */
55 static void do_benchmark_work(sk_tools::PictureRenderer* renderer,
56 int benchmarkType, const SkString *path, SkPicture* pic,
57 const int numRepeats, const char *msg, BenchTimer* timer) {
58 SkString msgPrefix;
59
60 switch (benchmarkType){
61 case kNormal_BenchmarkType:
62 msgPrefix.printf("Normal");
caryclark 2013/07/10 15:28:24 this may generate warnings on some platforms, that
sglez 2013/07/11 20:58:30 Thanks, I didn't know about that.
63 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kNone_BBox HierarchyType);
64 break;
65 case kRTree_BenchmarkType:
66 msgPrefix.printf("RTree");
67 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kRTree_BBo xHierarchyType);
68 break;
caryclark 2013/07/10 15:28:24 default: SkASSERT(0);
69 }
70
71 renderer->init(pic);
72
73 /**
74 * If the renderer is not tiled, assume we are measuring recording.
75 */
76 bool recording = (NULL == renderer->getTiledRenderer());
77
78 SkDebugf("%s %s %s %d times...\n", msgPrefix.c_str(), msg, path->c_str(), nu mRepeats);
79 for (int i = 0; i < numRepeats; ++i) {
80 renderer->setup();
81 // Render once to fill caches.
82 renderer->render(NULL);
83 // Render again to measure
84 timer->start();
85 bool result = renderer->render(NULL);
86 timer->end();
87 // We only care about a false result on playback. RecordPictureRenderer: :render will always
88 // return false because we are passing a NULL ptr.
89 if(!recording && !result) {
90 SkDebugf("Error rendering (playback).\n");
91 }
92 }
93 renderer->end();
94 }
95
96 /**
97 * Call do_benchmark_work with a tiled renderer using the default tile dimension s.
98 */
99 static void benchmark_playback(
100 BenchmarkType benchmarkType, const int tileSize[2],
101 const SkString* path, SkPicture* pic, BenchTimer* timer) {
102 sk_tools::TiledPictureRenderer renderer;
103
104 SkString message("tiled_playback");
105 message.appendf("_%dx%d", tileSize[0], tileSize[1]);
106 do_benchmark_work(&renderer, benchmarkType,
107 path, pic, kNumPlaybacks, message.c_str(), timer);
108 }
109
110 /**
111 * Call do_benchmark_work with a RecordPictureRenderer.
112 */
113 static void benchmark_recording(
114 BenchmarkType benchmarkType, const int tileSize[2],
115 const SkString* path, SkPicture* pic, BenchTimer* timer) {
116 sk_tools::RecordPictureRenderer renderer;
117 do_benchmark_work(&renderer, benchmarkType, path, pic, kNumRecordings, "reco rding", timer);
118 }
119
120 static const SkString perIterTimeFormat("%f");
121 static const SkString normalTimeFormat("%f");
122
123 /**
124 * Takes argc,argv along with one of the benchmark functions defined above.
125 * Will loop along all skp files and perform measurments.
126 *
127 * Returns a SkScalar representing CPU time taken during benchmark.
128 * As a side effect, it spits the timer result to stdout.
129 * Will return -1.0 on error.
130 */
131 static SkScalar benchmark_loop(
132 int argc,
133 char **argv,
134 void (*func)(BenchmarkType, const int[], const SkString*, SkPicture*, Be nchTimer*),
caryclark 2013/07/10 15:28:24 skia's convention is to pass const SkString&
sglez 2013/07/11 20:58:30 Sorry about that, I changed some more cases where
135 const int tileSize[2],
136 Histogram histogram[],
137 BenchmarkType benchmarkType,
138 const char* configName) {
139 TimerData timerData(perIterTimeFormat, normalTimeFormat);
140 for (int index = 1; index < argc; ++index) {
141 BenchTimer timer;
142 SkString path(argv[index]);
143 SkAutoTUnref<SkPicture> pic(pic_from_path(argv[index]));
144 if (NULL != pic) {
145 func(benchmarkType, tileSize, &path, pic, &timer);
146 }
147 timerData.appendTimes(&timer, index == argc - 1);
148
149 histogram[index - 1].pathIndex = index;
150 histogram[index - 1].cpuTime = timer.fCpu;
caryclark 2013/07/10 15:28:24 if the pic == NULL, what is timer.fCpu? Does it ma
sglez 2013/07/11 20:58:30 This was more a lack of logic on my part than anyt
151 }
152
153 const SkString timerResult = timerData.getResult(
154 /*logPerIter = */ false,
155 /*printMin = */ false,
156 /*repeatDraw = */ 1,
157 /*configName = */ configName,
158 /*showWallTime = */ false,
159 /*showTruncatedWallTime = */ false,
160 /*showCpuTime = */ true,
161 /*showTruncatedCpuTime = */ false,
162 /*showGpuTime = */ false);
163
164 const char findStr[] = "= ";
165 int pos = timerResult.find(findStr);
166 if (-1 == pos) {
167 SkDebugf("Unexpected output from TimerData::getResult(...). Unable to pa rse.");
168 return -1.0;
169 }
170 SkDebugf("%s\n", timerResult.c_str());
171
172 SkScalar cpuTime = atof(timerResult.c_str() + pos + sizeof(findStr) - 1);
173 if (cpuTime == SkIntToScalar(0)) { // atof returns 0.0 on error.
174 SkDebugf("Unable to read value from timer result.\n");
175 return -1.0;
caryclark 2013/07/10 15:28:24 return SkIntToScalar(-1);
176 }
177 return cpuTime;
178 }
179
180 static int tool_main(int argc, char** argv) {
181 SkAutoGraphics ag;
182 SkString usage;
183 usage.printf("Usage: filename [filename]*\n");
184
185 if (argc < 2) {
186 SkDebugf("%s\n", usage.c_str());
187 return 0;
188 }
189
190 static const int tileSizes[kNumTileSizes][2] = {
191 {256, 256},
192 {512, 512},
193 {1024, 1024},
194 };
caryclark 2013/07/10 15:28:24 this seems unnecessarily fragile and will make add
195 static const int kNumBenchmarks = 6;
caryclark 2013/07/10 15:28:24 static const size_t kNumBenchmarks = 3 + kNumTileS
196 static const char* benchNames[kNumBenchmarks] = {
197 "normal_recording",
198 "normal_playback",
199 "rtree_recording",
200 "rtree_playback_256x256",
201 "rtree_playback_512x512",
202 "rtree_playback_1024x1024",
caryclark 2013/07/10 15:28:24 build these rtree_playback strings from the tile s
203 };
204 static SkScalar results[kNumBenchmarks];
205 static Histogram *histograms[kNumBenchmarks] = {
206 SkNEW_ARRAY(Histogram, argc - 1), // normal_recording
207 SkNEW_ARRAY(Histogram, argc - 1), // normal_playback
208 SkNEW_ARRAY(Histogram, argc - 1), // rtree_recording
209 SkNEW_ARRAY(Histogram, argc - 1), // rtree_playback_256x256
210 SkNEW_ARRAY(Histogram, argc - 1), // rtree_playback_512x512
211 SkNEW_ARRAY(Histogram, argc - 1), // rtree_playback_1024x1024
212 };
caryclark 2013/07/10 15:28:24 defer array initialization until you use it below
213 static void (*benchmarkFunctions[kNumBenchmarks])
214 (BenchmarkType, const int [kNumBenchmarks], const SkString*, SkPicture*, BenchTimer*) = {
215 benchmark_recording, // normal_recording
216 benchmark_playback, // normal_playback
217 benchmark_recording, // rtree_recording
caryclark 2013/07/10 15:28:24 change this to only contain the first 3 entries so
218 benchmark_playback, // rtree_playback_256x256
219 benchmark_playback, // rtree_playback_512x512
220 benchmark_playback, // rtree_playback_1024x1024
221 };
222
223 for (int i = 0; i < kNumBenchmarks; ++i) {
224 BenchmarkType type;
225 if (i < 2) {
226 type = kNormal_BenchmarkType;
227 } else {
228 type = kRTree_BenchmarkType;
229 }
230 int tileSize[2] = {256, 256};
231 // If our bencmark is of the type rtree_playback_[SIZE]. Set tileSize.
232 if (i >= 3) {
233 tileSize[0] = tileSizes[i - 3][0];
234 tileSize[1] = tileSizes[i - 3][1];
caryclark 2013/07/10 15:28:24 add benchmarkFunction = benchmark_playback;
235 }
caryclark 2013/07/10 15:28:24 add histograms[i] = SkNEW_ARRAY(...
236 results[i] = benchmark_loop(
237 argc, argv, benchmarkFunctions[i], tileSize, histograms[i],
238 type, benchNames[i]);
239 }
240
241 // Print results
242 SkDebugf("\n");
243 for (int i = 0; i < kNumBenchmarks; ++i) {
244 SkDebugf("%s total: %f\n", benchNames[i], results[i]);
245 }
246
247 // Print a rough analysis to stdout:
248 {
249 SkScalar normalRecordResult = results[0];
250 SkScalar normalPlaybackResult = results[1];
251 SkScalar rtreeRecordResult = results[2];
252 SkScalar rtreePlaybackResult = results[3];
253 SkASSERT(normalRecordResult != 0 && normalPlaybackResult != 0);
254 SkDebugf("\n");
255 SkDebugf("Recording: Relative difference: %.4f\n",
256 rtreeRecordResult / normalRecordResult);
257 SkDebugf("Playback (256x256): Relative difference: %.4f\n",
258 rtreePlaybackResult / normalPlaybackResult);
259 SkScalar times =
260 (kNumPlaybacks * (normalRecordResult - rtreeRecordResult)) /
261 (kNumRecordings * (rtreePlaybackResult - normalPlaybackResult));
262 SkDebugf("Number of playback repetitions for RTree to be worth it: %d (r atio: %.4f)\n",
263 SkScalarCeilToInt(times), times);
264 }
265
266 // Print min/max times for each benchmark.
267 SkDebugf("\n");
268 SkScalar minMax[][2] = {
caryclark 2013/07/10 15:28:24 minMax[kNumBenchmarks][... then remove initializ
269 // MIN MAX
270 {SK_ScalarMax, 0}, // normal_recording
271 {SK_ScalarMax, 0}, // normal_playback
272 {SK_ScalarMax, 0}, // rtree_recording
273 {SK_ScalarMax, 0}, // rtree_playback_256x256
274 {SK_ScalarMax, 0}, // rtree_playback_512x512
275 {SK_ScalarMax, 0}, // rtree_playback_1024x1024
276 };
277 for (int i = 0; i < argc - 1; ++i) {
278 for (int j = 0; j < kNumBenchmarks; ++j) {
caryclark 2013/07/10 15:28:24 reverse loop nesting so you can initialize minMax
279 SkScalar value = histograms[j][i].cpuTime;
280 if (value < minMax[j][0]) {
281 minMax[j][0] = value;
282 }
283 if (value > minMax[j][1]) {
284 minMax[j][1] = value;
285 }
286 }
287 }
288 for (int i = 0; i < kNumBenchmarks; ++i) {
caryclark 2013/07/10 15:28:24 reversing the loop nesting above allows this to be
289 SkString out;
290 out.printf("%s min is ", benchNames[i]);
291 out.appendf("%f\n", minMax[i][0]);
292 out.appendf("%s max is ", benchNames[i]);
293 out.appendf("%f\n", minMax[i][1]);
294 SkDebugf("%s", out.c_str());
caryclark 2013/07/10 15:28:24 is this more clear to you than if you replaced the
sglez 2013/07/11 20:58:30 SkDebugf is much clearer
295 }
296
297 // Output gnuplot readable histogram data..
298 const char* pbTitle = "bbh_shootout_playback.dat";
299 const char* recTitle = "bbh_shootout_record.dat";
300 SkFILEWStream playbackOut(pbTitle);
301 SkFILEWStream recordOut(recTitle);
302 recordOut.writeText("# Index Normal RTree\n");
303 playbackOut.writeText("# Index Normal RTree\n");
304 for (int i = 0; i < argc - 1; ++i) {
305 SkString pbLine;
306 SkString recLine;
307 // ==== Write record info
308 recLine.printf("%d ", i);
309 recLine.appendf("%f ", histograms[0][i].cpuTime); // Append normal_reco rd time
310 recLine.appendf("%f ", histograms[2][i].cpuTime); // Append rtree_recor d time
311
312 // ==== Write playback info
313 pbLine.printf("%d ", i);
314 pbLine.appendf("%f ", histograms[1][i].cpuTime); // Start with normal p layback time.
315 // Append all playback benchmark times.
316 for (int j = 3; j < kNumBenchmarks; ++j) {
caryclark 2013/07/10 15:28:24 replace 3 with some const defined above.
317 pbLine.appendf("%f ", histograms[j][i].cpuTime);
318 }
319 pbLine.appendf("\n");
320 recLine.appendf("\n");
321 playbackOut.writeText(pbLine.c_str());
322 recordOut.writeText(recLine.c_str());
323 }
324 SkDebugf("Wrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle) ;
325
326 for (int i = 0; i < kNumBenchmarks; ++i) {
327 SkDELETE(histograms[i]);
caryclark 2013/07/10 15:28:24 this suggests that some auto use is missing here
sglez 2013/07/11 20:58:30 I am now using SkTArrays
328 }
329
330 return 0;
331 }
332
333 int main(int argc, char** argv) {
334 return tool_main(argc, argv);
335 }
336
OLDNEW
« no previous file with comments | « gyp/tools.gyp ('k') | tools/lua/bbh_filter.lua » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698