Change JSON output of nanobench.
[platform/upstream/libSkiaSharp.git] / bench / ResultsWriter.h
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  * Classes for writing out bench results in various formats.
8  */
9
10 #ifndef SkResultsWriter_DEFINED
11 #define SkResultsWriter_DEFINED
12
13 #include "BenchLogger.h"
14 #include "SkJSONCPP.h"
15 #include "SkStream.h"
16 #include "SkString.h"
17 #include "SkTArray.h"
18 #include "SkTypes.h"
19
20 /**
21  * Base class for writing out the bench results.
22  *
23  * TODO(jcgregorio) Add info if tests fail to converge?
24  */
25 class ResultsWriter : SkNoncopyable {
26 public:
27     virtual ~ResultsWriter() {};
28
29     // Records one key value pair that makes up a unique identifier for this run.
30     // All keys must be set before calling bench().
31     virtual void key(const char name[], const char value[]) = 0;
32
33     // Records one option set for this run. All options must be set before
34     // calling bench().
35     virtual void option(const char name[], const char value[]) = 0;
36
37     // Denotes the start of a specific benchmark. Once bench is called,
38     // then config and timer can be called multiple times to record runs.
39     virtual void bench(const char name[], int32_t x, int32_t y) = 0;
40
41     // Records the specific configuration a bench is run under, such as "8888".
42     virtual void config(const char name[]) = 0;
43
44     // Records the options for a configuration, such as "GL_RENDERER".
45     virtual void configOption(const char name[], const char* value) = 0;
46
47     // Records a single test metric.
48     virtual void timer(const char name[], double ms) = 0;
49
50     // Call when all results are finished.
51     virtual void end() = 0;
52 };
53
54 /**
55  * This ResultsWriter handles writing out the human readable format of the
56  * bench results.
57  */
58 class LoggerResultsWriter : public ResultsWriter {
59 public:
60     explicit LoggerResultsWriter(BenchLogger& logger, const char* timeFormat)
61         : fLogger(logger)
62         , fTimeFormat(timeFormat) {
63         fLogger.logProgress("skia bench:");
64     }
65     virtual void key(const char name[], const char value[]) {
66         // Don't log keys to keep microbench output unchanged.
67     }
68     virtual void option(const char name[], const char value[]) {
69         fLogger.logProgress(SkStringPrintf(" %s=%s", name, value));
70     }
71     virtual void bench(const char name[], int32_t x, int32_t y) {
72         fLogger.logProgress(SkStringPrintf(
73             "\nrunning bench [%3d %3d] %40s", x, y, name));
74     }
75     virtual void config(const char name[]) {
76         fLogger.logProgress(SkStringPrintf("   %s:", name));
77     }
78     virtual void configOption(const char name[], const char* value) {
79         // Don't log configOptions to keep microbench output unchanged.
80     }
81     virtual void timer(const char name[], double ms) {
82         fLogger.logProgress(SkStringPrintf("  %s = ", name));
83         fLogger.logProgress(SkStringPrintf(fTimeFormat, ms));
84     }
85     virtual void end() {
86         fLogger.logProgress("\n");
87     }
88 private:
89     BenchLogger& fLogger;
90     const char* fTimeFormat;
91 };
92
93 /**
94  * This ResultsWriter handles writing out the results in JSON.
95  *
96  * The output looks like (except compressed to a single line):
97  *
98  *  {
99  *   "options" : {
100  *      "alpha" : "0xFF",
101  *      "scale" : "0",
102  *      ...
103  *      "system" : "UNIX"
104  *   },
105  *   "results" : [
106  *      {
107  *      "name" : "Xfermode_Luminosity_640_480",
108  *      "results" : [
109  *         {
110  *            "name": "565",
111  *            "cmsecs" : 143.188128906250,
112  *            "msecs" : 143.835957031250
113  *         },
114  *         ...
115  */
116
117 Json::Value* SkFindNamedNode(Json::Value* root, const char name[]);
118 Json::Value SkMakeBuilderJSON(const SkString &buildername);
119
120 class JSONResultsWriter : public ResultsWriter {
121 public:
122     explicit JSONResultsWriter(const char filename[])
123         : fFilename(filename)
124         , fRoot()
125         , fResults(fRoot["results"])
126         , fBench(NULL)
127         , fConfig(NULL) {
128     }
129     virtual void key(const char name[], const char value[]) {
130     }
131     virtual void option(const char name[], const char value[]) {
132         fRoot["options"][name] = value;
133     }
134     virtual void bench(const char name[], int32_t x, int32_t y) {
135         SkString sk_name(name);
136         sk_name.append("_");
137         sk_name.appendS32(x);
138         sk_name.append("_");
139         sk_name.appendS32(y);
140         Json::Value* bench_node = SkFindNamedNode(&fResults, sk_name.c_str());
141         fBench = &(*bench_node)["results"];
142     }
143     virtual void config(const char name[]) {
144         SkASSERT(NULL != fBench);
145         fConfig = SkFindNamedNode(fBench, name);
146     }
147     virtual void configOption(const char name[], const char* value) {
148     }
149     virtual void timer(const char name[], double ms) {
150         SkASSERT(NULL != fConfig);
151         (*fConfig)[name] = ms;
152     }
153     virtual void end() {
154         SkFILEWStream stream(fFilename.c_str());
155         stream.writeText(Json::FastWriter().write(fRoot).c_str());
156         stream.flush();
157     }
158 private:
159
160     SkString fFilename;
161     Json::Value fRoot;
162     Json::Value& fResults;
163     Json::Value* fBench;
164     Json::Value* fConfig;
165 };
166
167 /**
168  NanoJSONResultsWriter writes the test results out in the following
169  format:
170
171  {
172     "key": {
173       "arch": "Arm7",
174       "gpu": "SGX540",
175       "os": "Android",
176       "model": "GalaxyNexus",
177     }
178     "options": {
179        "GL_Version": "3.1",
180        ...
181     },
182     "gitHash": "d1830323662ae8ae06908b97f15180fd25808894",
183     "results" : {
184         "Xfermode_Luminosity_640_480" : {
185            "8888" : {
186                  "median_ms" : 143.188128906250,
187                  "min_ms" : 143.835957031250,
188                  ...
189               },
190           ...
191 */
192 class NanoJSONResultsWriter : public ResultsWriter {
193 public:
194     explicit NanoJSONResultsWriter(const char filename[], const char gitHash[])
195         : fFilename(filename)
196         , fRoot()
197         , fResults(fRoot["results"])
198         , fBench(NULL)
199         , fConfig(NULL) {
200         fRoot["gitHash"] = gitHash;
201     }
202     virtual void key(const char name[], const char value[]) {
203         fRoot["key"][name] = value;
204     }
205     virtual void option(const char name[], const char value[]) {
206         fRoot["options"][name] = value;
207     }
208     virtual void bench(const char name[], int32_t x, int32_t y) {
209         SkString id = SkStringPrintf( "%s_%d_%d", name, x, y);
210         fResults[id.c_str()] = Json::Value(Json::objectValue);
211         fBench = &fResults[id.c_str()];
212     }
213     virtual void config(const char name[]) {
214         SkASSERT(NULL != fBench);
215         fConfig = &(*fBench)[name];
216     }
217     virtual void configOption(const char name[], const char* value) {
218         (*fConfig)["options"][name] = value;
219     }
220     virtual void timer(const char name[], double ms) {
221         // Don't record if nan, or -nan.
222         if (sk_double_isnan(ms)) {
223             return;
224         }
225         SkASSERT(NULL != fConfig);
226         (*fConfig)[name] = ms;
227     }
228     virtual void end() {
229         SkFILEWStream stream(fFilename.c_str());
230         stream.writeText(Json::FastWriter().write(fRoot).c_str());
231         stream.flush();
232     }
233 private:
234
235     SkString fFilename;
236     Json::Value fRoot;
237     Json::Value& fResults;
238     Json::Value* fBench;
239     Json::Value* fConfig;
240 };
241
242
243 /**
244  * This ResultsWriter writes out to multiple ResultsWriters.
245  */
246 class MultiResultsWriter : public ResultsWriter {
247 public:
248     MultiResultsWriter() : writers() {
249     };
250     void add(ResultsWriter* writer) {
251       writers.push_back(writer);
252     }
253     virtual void key(const char name[], const char value[]) {
254         for (int i = 0; i < writers.count(); ++i) {
255             writers[i]->key(name, value);
256         }
257     }
258     virtual void option(const char name[], const char value[]) {
259         for (int i = 0; i < writers.count(); ++i) {
260             writers[i]->option(name, value);
261         }
262     }
263     virtual void bench(const char name[], int32_t x, int32_t y) {
264         for (int i = 0; i < writers.count(); ++i) {
265             writers[i]->bench(name, x, y);
266         }
267     }
268     virtual void config(const char name[]) {
269         for (int i = 0; i < writers.count(); ++i) {
270             writers[i]->config(name);
271         }
272     }
273     virtual void configOption(const char name[], const char* value) {
274         for (int i = 0; i < writers.count(); ++i) {
275             writers[i]->configOption(name, value);
276         }
277     }
278     virtual void timer(const char name[], double ms) {
279         for (int i = 0; i < writers.count(); ++i) {
280             writers[i]->timer(name, ms);
281         }
282     }
283     virtual void end() {
284         for (int i = 0; i < writers.count(); ++i) {
285             writers[i]->end();
286         }
287     }
288 private:
289     SkTArray<ResultsWriter *> writers;
290 };
291
292 /**
293  * Calls the end() method of T on destruction.
294  */
295 template <typename T> class CallEnd : SkNoncopyable {
296 public:
297     CallEnd(T& obj) : fObj(obj) {}
298     ~CallEnd() { fObj.end(); }
299 private:
300     T&  fObj;
301 };
302
303 #endif