Home | History | Annotate | Download | only in bench
      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:
     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.
     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.
     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.
     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".
     42     virtual void config(const char name[]) {}
     43 
     44     // Record the options for a configuration, such as "GL_RENDERER".
     45     virtual void configOption(const char name[], const char* value) {}
     46 
     47     // Record a single test metric.
     48     virtual void metric(const char name[], double ms) {}
     49 
     50     // Record a list of test metrics.
     51     virtual void metrics(const char name[], const SkTArray<double>& array) {}
     52 
     53     // Flush to storage now please.
     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:
     81     explicit NanoJSONResultsWriter(const char filename[])
     82         : fFilename(filename)
     83         , fRoot()
     84         , fResults(fRoot["results"])
     85         , fBench(nullptr)
     86         , fConfig(nullptr) {}
     87 
     88     ~NanoJSONResultsWriter() override {
     89         this->flush();
     90     }
     91 
     92     // Added under "key".
     93     void key(const char name[], const char value[]) override {
     94         fRoot["key"][name] = value;
     95     }
     96     // Inserted directly into the root.
     97     void property(const char name[], const char value[]) override {
     98         fRoot[name] = value;
     99     }
    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     }
    105     void config(const char name[]) override {
    106         SkASSERT(fBench);
    107         fConfig = &(*fBench)[name];
    108     }
    109     void configOption(const char name[], const char* value) override {
    110         (*fConfig)["options"][name] = value;
    111     }
    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     }
    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.
    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