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 "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 | |
OLD | NEW |