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 | |
25 enum BenchmarkType { | |
26 kNormal_BenchmarkType = 0, | |
27 kRTree_BenchmarkType, | |
28 }; | |
29 | |
30 struct Histogram { | |
caryclark
2013/07/12 13:13:28
Add
Histogram()
: fCpuTime(SkIntToScalar(-1))
| |
31 SkScalar fCpuTime; | |
32 SkString fPath; | |
33 }; | |
34 | |
35 // Defined in PictureRenderingFlags.cpp | |
36 extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap ); | |
caryclark
2013/07/12 13:13:28
since it's also used by bench_pictures_main.cpp, l
sglez
2013/07/13 03:57:15
CL sent
| |
37 | |
38 static SkPicture* pic_from_path(const char path[]) { | |
39 SkFILEStream stream(path); | |
40 if (!stream.isValid()) { | |
41 SkDebugf("-- Can't open '%s'\n", path); | |
42 return NULL; | |
43 } | |
44 return SkPicture::CreateFromStream(&stream, &lazy_decode_bitmap); | |
45 } | |
46 | |
47 /** | |
48 * This function is the sink to which all work ends up going. | |
49 * Renders the picture into the renderer. It may or may not use an RTree. | |
50 * The renderer is chosen upstream. If we want to measure recording, we will | |
51 * use a RecordPictureRenderer. If we want to measure rendering, we eill use a | |
52 * TiledPictureRenderer. | |
53 */ | |
54 static void do_benchmark_work(sk_tools::PictureRenderer* renderer, | |
55 int benchmarkType, const SkString& path, SkPicture* pic, | |
56 const int numRepeats, const char *msg, BenchTimer* timer) { | |
57 SkString msgPrefix; | |
58 | |
59 switch (benchmarkType){ | |
60 case kNormal_BenchmarkType: | |
61 msgPrefix.set("Normal"); | |
62 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kNone_BBox HierarchyType); | |
63 break; | |
64 case kRTree_BenchmarkType: | |
65 msgPrefix.set("RTree"); | |
66 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kRTree_BBo xHierarchyType); | |
67 break; | |
68 default: | |
69 SkASSERT(0); | |
70 break; | |
71 } | |
72 | |
73 renderer->init(pic); | |
74 | |
75 /** | |
76 * If the renderer is not tiled, assume we are measuring recording. | |
77 */ | |
78 bool recording = (NULL == renderer->getTiledRenderer()); | |
79 | |
80 SkDebugf("%s %s %s %d times...\n", msgPrefix.c_str(), msg, path.c_str(), num Repeats); | |
81 for (int i = 0; i < numRepeats; ++i) { | |
82 renderer->setup(); | |
83 // Render once to fill caches. | |
84 renderer->render(NULL); | |
85 // Render again to measure | |
86 timer->start(); | |
87 bool result = renderer->render(NULL); | |
88 timer->end(); | |
89 // We only care about a false result on playback. RecordPictureRenderer: :render will always | |
90 // return false because we are passing a NULL ptr. | |
91 if(!recording && !result) { | |
92 SkDebugf("Error rendering (playback).\n"); | |
caryclark
2013/07/12 13:13:28
If recording == false, this will print the debug m
sglez
2013/07/13 03:57:15
RecordPictureRenderer::render(..), returns false w
caryclark
2013/07/15 12:00:15
My mistake. I misread the && for an ||.
On 2013/0
| |
93 } | |
94 } | |
95 renderer->end(); | |
96 } | |
97 | |
98 /** | |
99 * Call do_benchmark_work with a tiled renderer using the default tile dimension s. | |
100 */ | |
101 static void benchmark_playback( | |
102 BenchmarkType benchmarkType, const int tileSize[2], | |
103 const SkString& path, SkPicture* pic, BenchTimer* timer) { | |
104 sk_tools::TiledPictureRenderer renderer; | |
105 | |
106 SkString message("tiled_playback"); | |
107 message.appendf("_%dx%d", tileSize[0], tileSize[1]); | |
108 do_benchmark_work(&renderer, benchmarkType, | |
109 path, pic, kNumPlaybacks, message.c_str(), timer); | |
110 } | |
111 | |
112 /** | |
113 * Call do_benchmark_work with a RecordPictureRenderer. | |
114 */ | |
115 static void benchmark_recording( | |
116 BenchmarkType benchmarkType, const int tileSize[2], | |
117 const SkString& path, SkPicture* pic, BenchTimer* timer) { | |
118 sk_tools::RecordPictureRenderer renderer; | |
119 do_benchmark_work(&renderer, benchmarkType, path, pic, kNumRecordings, "reco rding", timer); | |
120 } | |
121 | |
122 static const SkString perIterTimeFormat("%f"); | |
123 static const SkString normalTimeFormat("%f"); | |
124 | |
125 /** | |
126 * Takes argc,argv along with one of the benchmark functions defined above. | |
127 * Will loop along all skp files and perform measurments. | |
128 * | |
129 * Returns a SkScalar representing CPU time taken during benchmark. | |
130 * As a side effect, it spits the timer result to stdout. | |
131 * Will return -1.0 on error. | |
132 */ | |
133 static SkScalar benchmark_loop( | |
134 int argc, | |
135 char **argv, | |
136 void (*func)(BenchmarkType, const int[], const SkString&, SkPicture*, Be nchTimer*), | |
caryclark
2013/07/12 13:13:28
below, you use const int[kNumBenchMarks]. Make the
sglez
2013/07/13 03:57:15
Yes, I should have typedef'd that some time ago.
| |
137 const int tileSize[2], | |
caryclark
2013/07/12 13:13:28
I assume the '2' is the number of tile dimensions,
sglez
2013/07/13 03:57:15
In this instance, I think having a struct would be
| |
138 SkTArray<Histogram>& histogram, | |
139 BenchmarkType benchmarkType, | |
140 const char* configName) { | |
141 TimerData timerData(perIterTimeFormat, normalTimeFormat); | |
142 for (int index = 1; index < argc; ++index) { | |
caryclark
2013/07/12 13:13:28
this seems inconsistent. If argc == 1, this loop e
sglez
2013/07/13 03:57:15
If argc == 1 this loop doesn't excecute :)
caryclark
2013/07/15 12:00:15
Understood -- my mistake again. See, aren't code r
| |
143 BenchTimer timer; | |
144 SkString path(argv[index]); | |
145 SkAutoTUnref<SkPicture> pic(pic_from_path(argv[index])); | |
146 if (NULL == pic) { | |
147 SkDebugf("Couldn't create picture. Ignoring path: %s\n", path.c_str( )); | |
caryclark
2013/07/12 13:13:28
Here you use path.c_str() for char* . A couple lin
| |
148 // Make fCpuTime negative so that we don't mess with stats: | |
149 histogram[index - 1].fCpuTime = SkIntToScalar(-1); | |
caryclark
2013/07/12 13:13:28
move this to the struct constructor (see above)
| |
150 continue; | |
151 } | |
152 func(benchmarkType, tileSize, path, pic, &timer); | |
caryclark
2013/07/12 13:13:28
(*func)(benchmarkType ...
| |
153 timerData.appendTimes(&timer, index == argc - 1); | |
caryclark
2013/07/12 13:13:28
this is a good place to reverse this to
argc - 1
| |
154 | |
155 histogram[index - 1].fPath = path; | |
156 histogram[index - 1].fCpuTime = timer.fCpu; | |
157 } | |
158 | |
159 const SkString timerResult = timerData.getResult( | |
160 /*logPerIter = */ false, | |
161 /*printMin = */ false, | |
162 /*repeatDraw = */ 1, | |
163 /*configName = */ configName, | |
164 /*showWallTime = */ false, | |
165 /*showTruncatedWallTime = */ false, | |
166 /*showCpuTime = */ true, | |
167 /*showTruncatedCpuTime = */ false, | |
168 /*showGpuTime = */ false); | |
169 | |
170 const char findStr[] = "= "; | |
171 int pos = timerResult.find(findStr); | |
172 if (-1 == pos) { | |
173 SkDebugf("Unexpected output from TimerData::getResult(...). Unable to pa rse."); | |
174 return SkIntToScalar(-1); | |
175 } | |
176 SkDebugf("%s\n", timerResult.c_str()); | |
177 | |
178 SkScalar cpuTime = atof(timerResult.c_str() + pos + sizeof(findStr) - 1); | |
179 if (cpuTime == SkIntToScalar(0)) { // atof returns 0.0 on error. | |
caryclark
2013/07/12 13:13:28
0 is always 0, so you can say
if (cpuTime == 0) {
| |
180 SkDebugf("Unable to read value from timer result.\n"); | |
181 return SkIntToScalar(-1); | |
182 } | |
183 return cpuTime; | |
184 } | |
185 | |
186 static int tool_main(int argc, char** argv) { | |
187 SkAutoGraphics ag; | |
188 SkString usage; | |
189 usage.printf("Usage: filename [filename]*\n"); | |
190 | |
191 if (argc < 2) { | |
192 SkDebugf("%s\n", usage.c_str()); | |
caryclark
2013/07/12 13:13:28
return something other than zero in the error case
| |
193 return 0; | |
194 } | |
195 | |
196 static const int tileSizes[][2] = { | |
197 {256, 256}, | |
198 {512, 512}, | |
199 {1024, 1024}, | |
200 }; | |
201 static const size_t kNumTileSizes = SK_ARRAY_COUNT(tileSizes); | |
202 static const size_t kNumBenchmarks = 3 + kNumTileSizes; | |
caryclark
2013/07/12 13:13:28
you want kNumBaseBenches (or another name of your
| |
203 static SkString benchNames[kNumBenchmarks]; | |
204 | |
205 benchNames[0] = "normal_recording"; | |
206 benchNames[1] = "normal_playback"; | |
207 benchNames[2] = "rtree_recording"; | |
208 for (size_t i = 0; i < kNumTileSizes; ++i) { | |
209 SkString benchName; | |
210 benchName.printf("rtree_playback_%dx%d", tileSizes[i][0], tileSizes[i][1 ]); | |
caryclark
2013/07/12 13:13:28
earlier, you set the name in the string and did ap
| |
211 benchNames[i + kNumTileSizes] = benchName.c_str(); | |
caryclark
2013/07/12 13:13:28
kNumTileSizes isn't what you want here. You need a
| |
212 } | |
213 static SkScalar results[kNumBenchmarks]; | |
214 static SkTArray<Histogram> histograms[kNumBenchmarks]; | |
215 | |
216 static void (*baseBenchmarkFunctions[3]) | |
217 (BenchmarkType, const int [kNumBenchmarks], const SkString&, SkPicture*, BenchTimer*) = { | |
218 benchmark_recording, // normal_recording | |
219 benchmark_playback, // normal_playback | |
220 benchmark_recording, // rtree_recording | |
221 }; | |
caryclark
2013/07/12 13:13:28
this is one possibility for
const int kNumBaseBen
| |
222 | |
223 for (size_t i = 0; i < kNumBenchmarks; ++i) { | |
224 BenchmarkType type; | |
225 if (i < 2) { | |
226 type = kNormal_BenchmarkType; | |
227 } else { | |
228 type = kRTree_BenchmarkType; | |
caryclark
2013/07/12 13:13:28
I'm not crazy about benchNames being initialized i
sglez
2013/07/13 03:57:15
This comment made me do a big change. I hope I was
| |
229 } | |
230 int tileSize[2] = {256, 256}; | |
231 static void (*benchmarkFunction) | |
232 (BenchmarkType, const int [kNumBenchmarks], const SkString&, SkPictu re*, BenchTimer*); | |
233 if (i < 3) { | |
234 benchmarkFunction = baseBenchmarkFunctions[i]; | |
235 } else { | |
236 // If our bencmark is of the type rtree_playback_[SIZE]: | |
237 // Set tileSize and set benchmark to playback. | |
238 tileSize[0] = tileSizes[i - 3][0]; | |
239 tileSize[1] = tileSizes[i - 3][1]; | |
caryclark
2013/07/12 13:13:28
lots of '3's here :)
| |
240 benchmarkFunction = benchmark_playback; | |
241 } | |
242 histograms[i] = SkTArray<Histogram>(argc - 1); | |
243 histograms[i].reset(argc - 1); | |
244 results[i] = benchmark_loop( | |
245 argc, argv, benchmarkFunction, tileSize, histograms[i], | |
246 type, benchNames[i].c_str()); | |
247 } | |
248 | |
249 // Print results | |
250 SkDebugf("\n"); | |
251 for (size_t i = 0; i < kNumBenchmarks; ++i) { | |
252 SkDebugf("%s total: %f\n", benchNames[i].c_str(), results[i]); | |
253 } | |
254 | |
255 // Print a rough analysis to stdout: | |
256 { | |
257 SkScalar normalRecordResult = results[0]; | |
258 SkScalar normalPlaybackResult = results[1]; | |
259 SkScalar rtreeRecordResult = results[2]; | |
260 SkScalar rtreePlaybackResult = results[3]; | |
261 SkASSERT(normalRecordResult != 0 && normalPlaybackResult != 0); | |
262 SkDebugf("\n"); | |
263 SkDebugf("Recording: Relative difference: %.4f\n", | |
264 rtreeRecordResult / normalRecordResult); | |
265 SkDebugf("Playback (256x256): Relative difference: %.4f\n", | |
266 rtreePlaybackResult / normalPlaybackResult); | |
267 SkScalar times = | |
268 (kNumPlaybacks * (normalRecordResult - rtreeRecordResult)) / | |
269 (kNumRecordings * (rtreePlaybackResult - normalPlaybackResult)); | |
caryclark
2013/07/12 13:13:28
what about if rtreePlaybackResult == normalPlaybac
sglez
2013/07/13 03:57:15
I missed this comment and didn't address it. I wil
| |
270 SkDebugf("Number of playback repetitions for RTree to be worth it: %d (r atio: %.4f)\n", | |
271 SkScalarCeilToInt(times), times); | |
272 } | |
273 | |
274 // Print min/max times for each benchmark. | |
275 SkDebugf("\n"); | |
276 SkScalar minMax[kNumBenchmarks][2]; | |
caryclark
2013/07/12 13:13:28
2 being one for min, one for max. Good place for a
| |
277 for (size_t i = 0; i < kNumBenchmarks; ++i) { | |
278 SkString minPath; | |
279 SkString maxPath; | |
280 minMax[i][0] = SK_ScalarMax; | |
281 minMax[i][1] = 0; | |
282 for (int j = 0; j < argc - 1; ++j) { | |
283 SkScalar value = histograms[i][j].fCpuTime; | |
284 if (value < 0) continue; // skp wasn't found or couldn't be read. | |
285 if (value < minMax[i][0]) { | |
286 minMax[i][0] = value; | |
caryclark
2013/07/12 13:13:28
I prefer the pattern of
if (a op b) {
a = b;
bu
sglez
2013/07/13 03:57:15
Normally I would agree, but when doing min/max I p
| |
287 minPath = histograms[i][j].fPath; | |
288 } | |
289 if (value > minMax[i][1]) { | |
290 minMax[i][1] = value; | |
291 maxPath = histograms[i][j].fPath; | |
292 } | |
293 } | |
294 SkDebugf("%s min is %.3f:\t\t%s\n" | |
295 "%s max is %.3f:\t\t%s\n", | |
296 benchNames[i].c_str(), minMax[i][0], minPath.c_str(), | |
297 benchNames[i].c_str(), minMax[i][1], maxPath.c_str()); | |
298 } | |
299 | |
300 // Output gnuplot readable histogram data.. | |
301 const char* pbTitle = "bbh_shootout_playback.dat"; | |
302 const char* recTitle = "bbh_shootout_record.dat"; | |
303 SkFILEWStream playbackOut(pbTitle); | |
304 SkFILEWStream recordOut(recTitle); | |
305 recordOut.writeText("# Index Normal RTree\n"); | |
306 playbackOut.writeText("# Index Normal RTree\n"); | |
307 for (int i = 0; i < argc - 1; ++i) { | |
308 SkString pbLine; | |
309 SkString recLine; | |
310 // ==== Write record info | |
311 recLine.printf("%d ", i); | |
312 recLine.appendf("%f ", histograms[0][i].fCpuTime); // Append normal_rec ord time | |
313 recLine.appendf("%f ", histograms[2][i].fCpuTime); // Append rtree_reco rd time | |
314 | |
315 // ==== Write playback info | |
316 pbLine.printf("%d ", i); | |
317 pbLine.appendf("%f ", histograms[1][i].fCpuTime); // Start with normal playback time. | |
318 // Append all playback benchmark times. | |
319 for (size_t j = kNumTileSizes; j < kNumBenchmarks; ++j) { | |
320 pbLine.appendf("%f ", histograms[j][i].fCpuTime); | |
caryclark
2013/07/12 13:13:28
this puts a final ' ' followed by a '\n'
sglez
2013/07/13 03:57:15
Added a line that trims the trailing space.
| |
321 } | |
322 pbLine.appendf("\n"); | |
323 recLine.appendf("\n"); | |
324 playbackOut.writeText(pbLine.c_str()); | |
325 recordOut.writeText(recLine.c_str()); | |
326 } | |
327 SkDebugf("Wrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle) ; | |
328 | |
329 return 0; | |
330 } | |
331 | |
332 int main(int argc, char** argv) { | |
333 return tool_main(argc, argv); | |
334 } | |
335 | |
OLD | NEW |