OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 * | 6 * |
7 */ | 7 */ |
8 | 8 |
9 #include "VisualLightweightBenchModule.h" | 9 #include "VisualLightweightBenchModule.h" |
10 | 10 |
11 #include "ProcStats.h" | 11 #include "ProcStats.h" |
12 #include "SkApplication.h" | 12 #include "SkApplication.h" |
13 #include "SkCanvas.h" | 13 #include "SkCanvas.h" |
14 #include "SkCommandLineFlags.h" | 14 #include "SkCommandLineFlags.h" |
15 #include "SkForceLinking.h" | 15 #include "SkForceLinking.h" |
16 #include "SkGraphics.h" | 16 #include "SkGraphics.h" |
17 #include "SkGr.h" | 17 #include "SkGr.h" |
18 #include "SkImageDecoder.h" | 18 #include "SkImageDecoder.h" |
19 #include "SkOSFile.h" | 19 #include "SkOSFile.h" |
20 #include "SkStream.h" | 20 #include "SkStream.h" |
21 #include "Stats.h" | 21 #include "Stats.h" |
22 #include "gl/GrGLInterface.h" | 22 #include "gl/GrGLInterface.h" |
23 | 23 |
24 __SK_FORCE_IMAGE_DECODER_LINKING; | 24 __SK_FORCE_IMAGE_DECODER_LINKING; |
25 | 25 |
26 // Between samples we reset context | 26 // Between samples we reset context |
27 // Between frames we swap buffers | 27 // Between frames we swap buffers |
28 | |
29 DEFINE_int32(maxWarmupFrames, 100, "maxmium frames to try and tune for sane timi
ngs"); | |
30 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); | |
31 DEFINE_int32(samples, 10, "Number of times to time each skp."); | |
32 DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample."); | |
33 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); | |
34 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); | 28 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); |
35 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); | 29 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); |
36 DEFINE_string(key, "", | 30 DEFINE_string(key, "", |
37 "Space-separated key/value pairs to add to JSON identifying this b
uilder."); | 31 "Space-separated key/value pairs to add to JSON identifying this b
uilder."); |
38 DEFINE_string(properties, "", | 32 DEFINE_string(properties, "", |
39 "Space-separated key/value pairs to add to JSON identifying this r
un."); | 33 "Space-separated key/value pairs to add to JSON identifying this r
un."); |
| 34 DEFINE_int32(samples, 10, "Number of times to time each skp."); |
40 | 35 |
41 static SkString humanize(double ms) { | 36 static SkString humanize(double ms) { |
42 if (FLAGS_verbose) { | 37 if (FLAGS_verbose) { |
43 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); | 38 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); |
44 } | 39 } |
45 return HumanizeMs(ms); | 40 return HumanizeMs(ms); |
46 } | 41 } |
47 | 42 |
48 #define HUMANIZE(time) humanize(time).c_str() | 43 #define HUMANIZE(time) humanize(time).c_str() |
49 | 44 |
50 // We draw a big nonAA path to warmup the gpu / cpu | |
51 class WarmupBench : public Benchmark { | |
52 public: | |
53 WarmupBench() { | |
54 make_path(fPath); | |
55 } | |
56 private: | |
57 static void make_path(SkPath& path) { | |
58 #include "BigPathBench.inc" | |
59 } | |
60 const char* onGetName() override { return "warmupbench"; } | |
61 void onDraw(int loops, SkCanvas* canvas) override { | |
62 SkPaint paint; | |
63 paint.setStyle(SkPaint::kStroke_Style); | |
64 paint.setStrokeWidth(2); | |
65 for (int i = 0; i < loops; i++) { | |
66 canvas->drawPath(fPath, paint); | |
67 } | |
68 } | |
69 SkPath fPath; | |
70 }; | |
71 | |
72 VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner) | 45 VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner) |
73 : fCurrentSample(0) | 46 : fCurrentSample(0) |
74 , fCurrentFrame(0) | |
75 , fLoops(1) | |
76 , fState(kWarmup_State) | |
77 , fBenchmark(nullptr) | |
78 , fOwner(SkRef(owner)) | 47 , fOwner(SkRef(owner)) |
79 , fResults(new ResultsWriter) { | 48 , fResults(new ResultsWriter) { |
80 fBenchmarkStream.reset(new VisualBenchmarkStream); | 49 fBenchmarkStream.reset(new VisualBenchmarkStream); |
81 | 50 |
82 // Print header | 51 // Print header |
83 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tbench\n"
, FLAGS_samples, | 52 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tbench\n"
, FLAGS_samples, |
84 "samples"); | 53 "samples"); |
85 | 54 |
86 // setup json logging if required | 55 // setup json logging if required |
87 if (!FLAGS_outResultsFile.isEmpty()) { | 56 if (!FLAGS_outResultsFile.isEmpty()) { |
(...skipping 11 matching lines...) Expand all Loading... |
99 if (1 == FLAGS_properties.count() % 2) { | 68 if (1 == FLAGS_properties.count() % 2) { |
100 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); | 69 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); |
101 } else { | 70 } else { |
102 for (int i = 1; i < FLAGS_properties.count(); i += 2) { | 71 for (int i = 1; i < FLAGS_properties.count(); i += 2) { |
103 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); | 72 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); |
104 } | 73 } |
105 } | 74 } |
106 } | 75 } |
107 | 76 |
108 inline void VisualLightweightBenchModule::renderFrame(SkCanvas* canvas) { | 77 inline void VisualLightweightBenchModule::renderFrame(SkCanvas* canvas) { |
109 fBenchmark->draw(fLoops, canvas); | 78 fBenchmark->draw(fTSM.loops(), canvas); |
110 canvas->flush(); | 79 canvas->flush(); |
111 fOwner->present(); | 80 fOwner->present(); |
112 } | 81 } |
113 | 82 |
114 void VisualLightweightBenchModule::printStats() { | 83 void VisualLightweightBenchModule::printStats() { |
115 const SkTArray<double>& measurements = fRecords.back().fMeasurements; | 84 const SkTArray<double>& measurements = fRecords.back().fMeasurements; |
116 const char* shortName = fBenchmark->getUniqueName(); | 85 const char* shortName = fBenchmark->getUniqueName(); |
117 | 86 |
118 // update log | 87 // update log |
119 // Note: We currently log only the minimum. It would be interesting to log
more information | 88 // Note: We currently log only the minimum. It would be interesting to log
more information |
(...skipping 13 matching lines...) Expand all Loading... |
133 if (FLAGS_verbose) { | 102 if (FLAGS_verbose) { |
134 for (int i = 0; i < measurements.count(); i++) { | 103 for (int i = 0; i < measurements.count(); i++) { |
135 SkDebugf("%s ", HUMANIZE(measurements[i])); | 104 SkDebugf("%s ", HUMANIZE(measurements[i])); |
136 } | 105 } |
137 SkDebugf("%s\n", shortName); | 106 SkDebugf("%s\n", shortName); |
138 } else { | 107 } else { |
139 const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean; | 108 const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean; |
140 SkDebugf("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\n", | 109 SkDebugf("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\n", |
141 sk_tools::getCurrResidentSetSizeMB(), | 110 sk_tools::getCurrResidentSetSizeMB(), |
142 sk_tools::getMaxResidentSetSizeMB(), | 111 sk_tools::getMaxResidentSetSizeMB(), |
143 fLoops, | 112 fTSM.loops(), |
144 HUMANIZE(stats.min), | 113 HUMANIZE(stats.min), |
145 HUMANIZE(stats.median), | 114 HUMANIZE(stats.median), |
146 HUMANIZE(stats.mean), | 115 HUMANIZE(stats.mean), |
147 HUMANIZE(stats.max), | 116 HUMANIZE(stats.max), |
148 stdDevPercent, | 117 stdDevPercent, |
149 stats.plot.c_str(), | 118 stats.plot.c_str(), |
150 shortName); | 119 shortName); |
151 } | 120 } |
152 } | 121 } |
153 | 122 |
154 bool VisualLightweightBenchModule::advanceRecordIfNecessary(SkCanvas* canvas) { | 123 bool VisualLightweightBenchModule::advanceRecordIfNecessary(SkCanvas* canvas) { |
155 if (!fBenchmark && fState == kWarmup_State) { | |
156 fOwner->clear(canvas, SK_ColorWHITE, 2); | |
157 fBenchmark.reset(new WarmupBench); | |
158 return true; | |
159 } | |
160 | |
161 if (fBenchmark) { | 124 if (fBenchmark) { |
162 return true; | 125 return true; |
163 } | 126 } |
164 | 127 |
165 fBenchmark.reset(fBenchmarkStream->next()); | 128 fBenchmark.reset(fBenchmarkStream->next()); |
166 if (!fBenchmark) { | 129 if (!fBenchmark) { |
167 return false; | 130 return false; |
168 } | 131 } |
169 | 132 |
170 fOwner->clear(canvas, SK_ColorWHITE, 2); | 133 fOwner->clear(canvas, SK_ColorWHITE, 2); |
171 | 134 |
172 fBenchmark->delayedSetup(); | 135 fBenchmark->delayedSetup(); |
173 fRecords.push_back(); | 136 fRecords.push_back(); |
174 | 137 |
175 // Log bench name | 138 // Log bench name |
176 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, | 139 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, |
177 fBenchmark->getSize().fY); | 140 fBenchmark->getSize().fY); |
178 return true; | 141 return true; |
179 } | 142 } |
180 | 143 |
181 inline void VisualLightweightBenchModule::nextState(State nextState) { | |
182 fState = nextState; | |
183 } | |
184 | |
185 void VisualLightweightBenchModule::perCanvasPreDraw(SkCanvas* canvas, State next
State) { | |
186 fBenchmark->perCanvasPreDraw(canvas); | |
187 fBenchmark->preDraw(canvas); | |
188 fCurrentFrame = 0; | |
189 this->nextState(nextState); | |
190 } | |
191 | |
192 void VisualLightweightBenchModule::warmup(SkCanvas* canvas) { | |
193 if (fCurrentFrame >= FLAGS_maxWarmupFrames) { | |
194 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); | |
195 fBenchmark.reset(nullptr); | |
196 this->resetTimingState(); | |
197 fLoops = 1; | |
198 } else { | |
199 bool isEven = (fCurrentFrame++ % 2) == 0; | |
200 if (isEven) { | |
201 fTimer.start(); | |
202 } else { | |
203 double elapsedMs = this->elapsed(); | |
204 if (elapsedMs < FLAGS_loopMs) { | |
205 fLoops *= 2; | |
206 } | |
207 fTimer = WallTimer(); | |
208 fOwner->reset(); | |
209 } | |
210 } | |
211 } | |
212 | |
213 void VisualLightweightBenchModule::preWarm(State nextState) { | |
214 if (fCurrentFrame >= FLAGS_gpuFrameLag) { | |
215 // we currently time across all frames to make sure we capture all GPU w
ork | |
216 this->nextState(nextState); | |
217 fCurrentFrame = 0; | |
218 fTimer.start(); | |
219 } else { | |
220 fCurrentFrame++; | |
221 } | |
222 } | |
223 | |
224 void VisualLightweightBenchModule::draw(SkCanvas* canvas) { | 144 void VisualLightweightBenchModule::draw(SkCanvas* canvas) { |
225 if (!this->advanceRecordIfNecessary(canvas)) { | 145 if (!this->advanceRecordIfNecessary(canvas)) { |
226 SkDebugf("Exiting VisualBench successfully\n"); | 146 SkDebugf("Exiting VisualBench successfully\n"); |
227 fOwner->closeWindow(); | 147 fOwner->closeWindow(); |
228 return; | 148 return; |
229 } | 149 } |
230 this->renderFrame(canvas); | 150 this->renderFrame(canvas); |
231 switch (fState) { | 151 TimingStateMachine::ParentEvents event = fTSM.nextFrame(canvas, fBenchmark); |
232 case kWarmup_State: { | 152 switch (event) { |
233 this->warmup(canvas); | 153 case TimingStateMachine::kReset_ParentEvents: |
| 154 fOwner->reset(); |
234 break; | 155 break; |
235 } | 156 case TimingStateMachine::kTiming_ParentEvents: |
236 case kPreWarmLoopsPerCanvasPreDraw_State: { | |
237 this->perCanvasPreDraw(canvas, kPreWarmLoops_State); | |
238 break; | 157 break; |
239 } | 158 case TimingStateMachine::kTimingFinished_ParentEvents: |
240 case kPreWarmLoops_State: { | 159 fOwner->reset(); |
241 this->preWarm(kTuneLoops_State); | 160 fRecords.back().fMeasurements.push_back(fTSM.lastMeasurement()); |
| 161 if (++fCurrentSample > FLAGS_samples) { |
| 162 this->printStats(); |
| 163 fTSM.nextBenchmark(canvas, fBenchmark); |
| 164 fCurrentSample = 0; |
| 165 fBenchmark.reset(nullptr); |
| 166 } |
242 break; | 167 break; |
243 } | |
244 case kTuneLoops_State: { | |
245 this->tuneLoops(); | |
246 break; | |
247 } | |
248 case kPreWarmTimingPerCanvasPreDraw_State: { | |
249 this->perCanvasPreDraw(canvas, kPreWarmTiming_State); | |
250 break; | |
251 } | |
252 case kPreWarmTiming_State: { | |
253 this->preWarm(kTiming_State); | |
254 break; | |
255 } | |
256 case kTiming_State: { | |
257 this->timing(canvas); | |
258 break; | |
259 } | |
260 } | 168 } |
261 } | |
262 | 169 |
263 inline double VisualLightweightBenchModule::elapsed() { | |
264 fTimer.end(); | |
265 return fTimer.fWall; | |
266 } | |
267 | |
268 void VisualLightweightBenchModule::resetTimingState() { | |
269 fCurrentFrame = 0; | |
270 fTimer = WallTimer(); | |
271 fOwner->reset(); | |
272 } | |
273 | |
274 inline void VisualLightweightBenchModule::tuneLoops() { | |
275 if (1 << 30 == fLoops) { | |
276 // We're about to wrap. Something's wrong with the bench. | |
277 SkDebugf("InnerLoops wrapped\n"); | |
278 fLoops = 1; | |
279 } else { | |
280 double elapsedMs = this->elapsed(); | |
281 if (elapsedMs > FLAGS_loopMs) { | |
282 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | |
283 } else { | |
284 fLoops *= 2; | |
285 this->nextState(kPreWarmLoops_State); | |
286 } | |
287 this->resetTimingState(); | |
288 } | |
289 } | |
290 | |
291 void VisualLightweightBenchModule::recordMeasurement() { | |
292 double measurement = this->elapsed() / (FLAGS_frames * fLoops); | |
293 fRecords.back().fMeasurements.push_back(measurement); | |
294 } | |
295 | |
296 void VisualLightweightBenchModule::postDraw(SkCanvas* canvas) { | |
297 fBenchmark->postDraw(canvas); | |
298 fBenchmark->perCanvasPostDraw(canvas); | |
299 fBenchmark.reset(nullptr); | |
300 fCurrentSample = 0; | |
301 fLoops = 1; | |
302 } | |
303 | |
304 inline void VisualLightweightBenchModule::timing(SkCanvas* canvas) { | |
305 if (fCurrentFrame >= FLAGS_frames) { | |
306 this->recordMeasurement(); | |
307 if (fCurrentSample++ >= FLAGS_samples) { | |
308 this->printStats(); | |
309 this->postDraw(canvas); | |
310 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); | |
311 } else { | |
312 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | |
313 } | |
314 this->resetTimingState(); | |
315 } else { | |
316 fCurrentFrame++; | |
317 } | |
318 } | 170 } |
319 | 171 |
320 bool VisualLightweightBenchModule::onHandleChar(SkUnichar c) { | 172 bool VisualLightweightBenchModule::onHandleChar(SkUnichar c) { |
321 return true; | 173 return true; |
322 } | 174 } |
OLD | NEW |