2 * Copyright 2014 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
7 * Classes for writing out bench results in various formats.
10 #ifndef SkPictureResultsWriter_DEFINED
11 #define SkPictureResultsWriter_DEFINED
14 #include "PictureRenderer.h"
15 #include "BenchLogger.h"
16 #include "ResultsWriter.h"
17 #include "SkJSONCPP.h"
21 #include "TimerData.h"
24 * Base class for writing picture bench results.
26 class PictureResultsWriter : SkNoncopyable {
28 enum TileFlags {kPurging, kAvg};
30 PictureResultsWriter() {}
31 virtual ~PictureResultsWriter() {}
33 virtual void bench(const char name[], int32_t x, int32_t y) = 0;
34 virtual void logRenderer(sk_tools::PictureRenderer *pr) = 0;
35 virtual void tileMeta(int x, int y, int tx, int ty) = 0;
36 virtual void addTileFlag(PictureResultsWriter::TileFlags flag) = 0;
37 virtual void tileData(
40 const TimerData::Result result,
42 int numInnerLoops = 1) = 0;
43 virtual void end() = 0;
47 * This class allows bench data to be piped into multiple
48 * PictureResultWriter classes. It does not own any classes
49 * passed to it, so the owner is required to manage any classes
50 * passed to PictureResultsMultiWriter */
51 class PictureResultsMultiWriter : public PictureResultsWriter {
53 PictureResultsMultiWriter()
55 void add(PictureResultsWriter* newWriter) {
56 fWriters.push_back(newWriter);
58 virtual ~PictureResultsMultiWriter() {}
59 void bench(const char name[], int32_t x, int32_t y) override {
60 for(int i=0; i<fWriters.count(); ++i) {
61 fWriters[i]->bench(name, x, y);
64 void logRenderer(sk_tools::PictureRenderer *pr) override {
65 for(int i=0; i<fWriters.count(); ++i) {
66 fWriters[i]->logRenderer(pr);
69 void tileMeta(int x, int y, int tx, int ty) override {
70 for(int i=0; i<fWriters.count(); ++i) {
71 fWriters[i]->tileMeta(x, y, tx, ty);
74 void addTileFlag(PictureResultsWriter::TileFlags flag) override {
75 for(int i=0; i<fWriters.count(); ++i) {
76 fWriters[i]->addTileFlag(flag);
79 virtual void tileData(
82 const TimerData::Result result,
84 int numInnerLoops = 1) override {
85 for(int i=0; i<fWriters.count(); ++i) {
86 fWriters[i]->tileData(data, format, result, timerTypes,
91 for(int i=0; i<fWriters.count(); ++i) {
96 SkTArray<PictureResultsWriter*> fWriters;
100 * Writes to BenchLogger to mimic original behavior
102 class PictureResultsLoggerWriter : public PictureResultsWriter {
104 void logProgress(const char str[]) {
105 if(fLogger != NULL) {
106 fLogger->logProgress(str);
110 PictureResultsLoggerWriter(BenchLogger* log)
111 : fLogger(log), fCurrentLine() {}
112 void bench(const char name[], int32_t x, int32_t y) override {
114 result.printf("running bench [%i %i] %s ", x, y, name);
115 this->logProgress(result.c_str());
117 void logRenderer(sk_tools::PictureRenderer* renderer) override {
118 fCurrentLine = renderer->getConfigName();
120 void tileMeta(int x, int y, int tx, int ty) override {
121 fCurrentLine.appendf(": tile [%i,%i] out of [%i,%i]", x, y, tx, ty);
123 void addTileFlag(PictureResultsWriter::TileFlags flag) override {
124 if(flag == PictureResultsWriter::kPurging) {
125 fCurrentLine.append(" <withPurging>");
126 } else if(flag == PictureResultsWriter::kAvg) {
127 fCurrentLine.append(" <averaged>");
130 virtual void tileData(
133 const TimerData::Result result,
135 int numInnerLoops = 1) override {
136 SkString results = data->getResult(format, result,
137 fCurrentLine.c_str(), timerTypes, numInnerLoops);
138 results.append("\n");
139 this->logProgress(results.c_str());
141 void end() override {}
143 BenchLogger* fLogger;
144 SkString fCurrentLine;
148 * This PictureResultsWriter collects data in a JSON node
150 * The format is something like
154 * name: "Name_of_test"
157 * name: "Name of the configuration"
161 * purging: true //Flags for the current tile
165 * wsecs: [....] //Actual data ends up here
175 class PictureJSONResultsWriter : public PictureResultsWriter {
177 PictureJSONResultsWriter(const char filename[],
178 const char builderName[],
181 const char gitHash[],
183 : fStream(filename) {
184 fBuilderName = SkString(builderName);
185 fBuildNumber = buildNumber;
186 fTimestamp = timestamp;
187 fGitHash = SkString(gitHash);
188 fGitNumber = gitNumber;
189 fBuilderData = this->makeBuilderJson();
192 void bench(const char name[], int32_t x, int32_t y) override {
193 fBenchName = SkString(name);
195 void logRenderer(sk_tools::PictureRenderer* pr) override {
196 fParams = pr->getJSONConfig();
197 fConfigString = pr->getConfigName();
199 // Apparently tiles aren't used, so tileMeta is empty
200 void tileMeta(int x, int y, int tx, int ty) override {}
201 // Flags aren't used, so addTileFlag is empty
202 void addTileFlag(PictureResultsWriter::TileFlags flag) override {}
203 virtual void tileData(
206 const TimerData::Result result,
208 int numInnerLoops = 1) override {
209 Json::Value newData = data->getJSON(timerTypes, result, numInnerLoops);
210 Json::Value combinedParams(fBuilderData);
211 for(Json::ValueIterator iter = fParams.begin(); iter != fParams.end();
213 combinedParams[iter.key().asString()]= *iter;
215 // For each set of timer data
216 for(Json::ValueIterator iter = newData.begin(); iter != newData.end();
219 data["buildNumber"] = fBuildNumber;
220 data["timestamp"] = fTimestamp;
221 data["gitHash"] = fGitHash.c_str();
222 data["gitNumber"] = fGitNumber;
223 data["isTrybot"] = fBuilderName.endsWith("Trybot");
225 data["params"] = combinedParams;
226 data["params"]["benchName"] = fBenchName.c_str();
228 // Not including skpSize because that's deprecated?
229 data["key"] = this->makeKey(iter.key().asString().c_str()).c_str();
231 SkTArray<double> times;
232 Json::Value val = *iter;
233 for(Json::ValueIterator vals = val.begin(); vals != val.end();
235 times.push_back((*vals).asDouble());
237 qsort(static_cast<void*>(times.begin()), times.count(),
238 sizeof(double), PictureJSONResultsWriter::CompareDoubles);
239 data["value"] = times[static_cast<int>(times.count() * 0.25f)];
240 data["params"]["measurementType"] = iter.key().asString();
241 fStream.writeText(Json::FastWriter().write(data).c_str());
244 void end() override {
248 Json::Value makeBuilderJson() const {
249 static const int kNumKeys = 6;
250 static const char* kKeys[kNumKeys] = {
251 "role", "os", "model", "gpu", "arch", "configuration"};
252 Json::Value builderData;
254 if (!fBuilderName.isEmpty()) {
255 SkTArray<SkString> splitBuilder;
256 SkStrSplit(fBuilderName.c_str(), "-", &splitBuilder);
257 SkASSERT(splitBuilder.count() >= kNumKeys);
258 for (int i = 0; i < kNumKeys && i < splitBuilder.count(); ++i) {
259 builderData[kKeys[i]] = splitBuilder[i].c_str();
261 builderData["builderName"] = fBuilderName.c_str();
262 if (kNumKeys < splitBuilder.count()) {
264 for (int i = kNumKeys; i < splitBuilder.count(); ++i) {
265 extras.append(splitBuilder[i]);
266 if (i != splitBuilder.count() - 1) {
270 builderData["badParams"] = extras.c_str();
276 static int CompareDoubles(const void* p1, const void* p2) {
277 if(*static_cast<const double*>(p1) < *static_cast<const double*>(p2)) {
279 } else if(*static_cast<const double*>(p1) ==
280 *static_cast<const double*>(p2)) {
286 SkString makeKey(const char measurementType[]) const {
287 SkString tmp(fBuilderName);
289 tmp.append(fBenchName);
291 tmp.append(fConfigString);
293 tmp.append(measurementType);
297 SkFILEWStream fStream;
298 Json::Value fBuilderData;
302 SkString fConfigString;
303 SkString fBuilderName;