• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkOSFile.h"
16 #include "SkOSPath.h"
17 #include "SkStream.h"
18 #include "SkString.h"
19 #include "SkTypes.h"
20 
21 /**
22  * Base class for writing out the bench results.
23  *
24  * Default implementation does nothing.
25  */
26 class ResultsWriter : SkNoncopyable {
27 public:
~ResultsWriter()28     virtual ~ResultsWriter() {}
29 
30     // Record one key value pair that makes up a unique key for this type of run, e.g.
31     // builder name, machine type, Debug/Release, etc.
key(const char name[],const char value[])32     virtual void key(const char name[], const char value[]) {}
33 
34     // Record one key value pair that describes the run instance, e.g. git hash, build number.
property(const char name[],const char value[])35     virtual void property(const char name[], const char value[]) {}
36 
37     // Denote the start of a specific benchmark. Once bench is called,
38     // then config and metric can be called multiple times to record runs.
bench(const char name[],int32_t x,int32_t y)39     virtual void bench(const char name[], int32_t x, int32_t y) {}
40 
41     // Record the specific configuration a bench is run under, such as "8888".
config(const char name[])42     virtual void config(const char name[]) {}
43 
44     // Record the options for a configuration, such as "GL_RENDERER".
configOption(const char name[],const char * value)45     virtual void configOption(const char name[], const char* value) {}
46 
47     // Record a single test metric.
metric(const char name[],double ms)48     virtual void metric(const char name[], double ms) {}
49 
50     // Record a list of test metrics.
metrics(const char name[],const SkTArray<double> & array)51     virtual void metrics(const char name[], const SkTArray<double>& array) {}
52 
53     // Flush to storage now please.
flush()54     virtual void flush() {}
55 };
56 
57 /**
58  NanoJSONResultsWriter writes the test results out in the following
59  format:
60 
61  {
62     "key": {
63       "arch": "Arm7",
64       "gpu": "SGX540",
65       "os": "Android",
66       "model": "GalaxyNexus",
67     }
68     "gitHash": "d1830323662ae8ae06908b97f15180fd25808894",
69     "build_number": "1234",
70     "results" : {
71         "Xfermode_Luminosity_640_480" : {
72            "8888" : {
73                  "median_ms" : 143.188128906250,
74                  "min_ms" : 143.835957031250,
75                  ...
76               },
77           ...
78 */
79 class NanoJSONResultsWriter : public ResultsWriter {
80 public:
NanoJSONResultsWriter(const char filename[])81     explicit NanoJSONResultsWriter(const char filename[])
82         : fFilename(filename)
83         , fRoot()
84         , fResults(fRoot["results"])
85         , fBench(nullptr)
86         , fConfig(nullptr) {}
87 
~NanoJSONResultsWriter()88     ~NanoJSONResultsWriter() override {
89         this->flush();
90     }
91 
92     // Added under "key".
key(const char name[],const char value[])93     void key(const char name[], const char value[]) override {
94         fRoot["key"][name] = value;
95     }
96     // Inserted directly into the root.
property(const char name[],const char value[])97     void property(const char name[], const char value[]) override {
98         fRoot[name] = value;
99     }
bench(const char name[],int32_t x,int32_t y)100     void bench(const char name[], int32_t x, int32_t y) override {
101         SkString id = SkStringPrintf( "%s_%d_%d", name, x, y);
102         fResults[id.c_str()] = Json::Value(Json::objectValue);
103         fBench = &fResults[id.c_str()];
104     }
config(const char name[])105     void config(const char name[]) override {
106         SkASSERT(fBench);
107         fConfig = &(*fBench)[name];
108     }
configOption(const char name[],const char * value)109     void configOption(const char name[], const char* value) override {
110         (*fConfig)["options"][name] = value;
111     }
metric(const char name[],double ms)112     void metric(const char name[], double ms) override {
113         // Don't record if nan, or -nan.
114         if (sk_double_isnan(ms)) {
115             return;
116         }
117         SkASSERT(fConfig);
118         (*fConfig)[name] = ms;
119     }
metrics(const char name[],const SkTArray<double> & array)120     void metrics(const char name[], const SkTArray<double>& array) override {
121         SkASSERT(fConfig);
122         Json::Value value = Json::Value(Json::arrayValue);
123         value.resize(array.count());
124         for (int i = 0; i < array.count(); i++) {
125             // Don't care about nan-ness.
126             value[i] = array[i];
127         }
128         (*fConfig)[name] = std::move(value);
129     }
130 
131     // Flush to storage now please.
flush()132     void flush() override {
133         SkString dirname = SkOSPath::Dirname(fFilename.c_str());
134         if (!sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) {
135             if (!sk_mkdir(dirname.c_str())) {
136                 SkDebugf("Failed to create directory.");
137             }
138         }
139         SkFILEWStream stream(fFilename.c_str());
140         stream.writeText(Json::StyledWriter().write(fRoot).c_str());
141         stream.flush();
142     }
143 
144 private:
145     SkString fFilename;
146     Json::Value fRoot;
147     Json::Value& fResults;
148     Json::Value* fBench;
149     Json::Value* fConfig;
150 };
151 
152 
153 #endif
154