/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Classes for writing out bench results in various formats. */ #ifndef SkResultsWriter_DEFINED #define SkResultsWriter_DEFINED #include "BenchLogger.h" #include "SkJSONCPP.h" #include "SkStream.h" #include "SkString.h" #include "SkTArray.h" #include "SkTypes.h" /** * Base class for writing out the bench results. * * TODO(jcgregorio) Add info if tests fail to converge? */ class ResultsWriter : SkNoncopyable { public: virtual ~ResultsWriter() {}; // Records one key value pair that makes up a unique identifier for this run. // All keys must be set before calling bench(). virtual void key(const char name[], const char value[]) = 0; // Records one option set for this run. All options must be set before // calling bench(). virtual void option(const char name[], const char value[]) = 0; // Denotes the start of a specific benchmark. Once bench is called, // then config and timer can be called multiple times to record runs. virtual void bench(const char name[], int32_t x, int32_t y) = 0; // Records the specific configuration a bench is run under, such as "8888". virtual void config(const char name[]) = 0; // Records the options for a configuration, such as "GL_RENDERER". virtual void configOption(const char name[], const char* value) = 0; // Records a single test metric. virtual void timer(const char name[], double ms) = 0; // Call when all results are finished. virtual void end() = 0; }; /** * This ResultsWriter handles writing out the human readable format of the * bench results. */ class LoggerResultsWriter : public ResultsWriter { public: explicit LoggerResultsWriter(BenchLogger& logger, const char* timeFormat) : fLogger(logger) , fTimeFormat(timeFormat) { fLogger.logProgress("skia bench:"); } virtual void key(const char name[], const char value[]) { // Don't log keys to keep microbench output unchanged. } virtual void option(const char name[], const char value[]) { fLogger.logProgress(SkStringPrintf(" %s=%s", name, value)); } virtual void bench(const char name[], int32_t x, int32_t y) { fLogger.logProgress(SkStringPrintf( "\nrunning bench [%3d %3d] %40s", x, y, name)); } virtual void config(const char name[]) { fLogger.logProgress(SkStringPrintf(" %s:", name)); } virtual void configOption(const char name[], const char* value) { // Don't log configOptions to keep microbench output unchanged. } virtual void timer(const char name[], double ms) { fLogger.logProgress(SkStringPrintf(" %s = ", name)); fLogger.logProgress(SkStringPrintf(fTimeFormat, ms)); } virtual void end() { fLogger.logProgress("\n"); } private: BenchLogger& fLogger; const char* fTimeFormat; }; /** * This ResultsWriter handles writing out the results in JSON. * * The output looks like (except compressed to a single line): * * { * "options" : { * "alpha" : "0xFF", * "scale" : "0", * ... * "system" : "UNIX" * }, * "results" : [ * { * "name" : "Xfermode_Luminosity_640_480", * "results" : [ * { * "name": "565", * "cmsecs" : 143.188128906250, * "msecs" : 143.835957031250 * }, * ... */ Json::Value* SkFindNamedNode(Json::Value* root, const char name[]); Json::Value SkMakeBuilderJSON(const SkString &buildername); class JSONResultsWriter : public ResultsWriter { public: explicit JSONResultsWriter(const char filename[]) : fFilename(filename) , fRoot() , fResults(fRoot["results"]) , fBench(NULL) , fConfig(NULL) { } virtual void key(const char name[], const char value[]) { } virtual void option(const char name[], const char value[]) { fRoot["options"][name] = value; } virtual void bench(const char name[], int32_t x, int32_t y) { SkString sk_name(name); sk_name.append("_"); sk_name.appendS32(x); sk_name.append("_"); sk_name.appendS32(y); Json::Value* bench_node = SkFindNamedNode(&fResults, sk_name.c_str()); fBench = &(*bench_node)["results"]; } virtual void config(const char name[]) { SkASSERT(NULL != fBench); fConfig = SkFindNamedNode(fBench, name); } virtual void configOption(const char name[], const char* value) { } virtual void timer(const char name[], double ms) { SkASSERT(NULL != fConfig); (*fConfig)[name] = ms; } virtual void end() { SkFILEWStream stream(fFilename.c_str()); stream.writeText(Json::FastWriter().write(fRoot).c_str()); stream.flush(); } private: SkString fFilename; Json::Value fRoot; Json::Value& fResults; Json::Value* fBench; Json::Value* fConfig; }; /** NanoJSONResultsWriter writes the test results out in the following format: { "key": { "arch": "Arm7", "gpu": "SGX540", "os": "Android", "model": "GalaxyNexus", } "options": { "GL_Version": "3.1", ... }, "gitHash": "d1830323662ae8ae06908b97f15180fd25808894", "results" : { "Xfermode_Luminosity_640_480" : { "8888" : { "median_ms" : 143.188128906250, "min_ms" : 143.835957031250, ... }, ... */ class NanoJSONResultsWriter : public ResultsWriter { public: explicit NanoJSONResultsWriter(const char filename[], const char gitHash[]) : fFilename(filename) , fRoot() , fResults(fRoot["results"]) , fBench(NULL) , fConfig(NULL) { fRoot["gitHash"] = gitHash; } virtual void key(const char name[], const char value[]) { fRoot["key"][name] = value; } virtual void option(const char name[], const char value[]) { fRoot["options"][name] = value; } virtual void bench(const char name[], int32_t x, int32_t y) { SkString id = SkStringPrintf( "%s_%d_%d", name, x, y); fResults[id.c_str()] = Json::Value(Json::objectValue); fBench = &fResults[id.c_str()]; } virtual void config(const char name[]) { SkASSERT(NULL != fBench); fConfig = &(*fBench)[name]; } virtual void configOption(const char name[], const char* value) { (*fConfig)["options"][name] = value; } virtual void timer(const char name[], double ms) { // Don't record if nan, or -nan. if (sk_double_isnan(ms)) { return; } SkASSERT(NULL != fConfig); (*fConfig)[name] = ms; } virtual void end() { SkFILEWStream stream(fFilename.c_str()); stream.writeText(Json::FastWriter().write(fRoot).c_str()); stream.flush(); } private: SkString fFilename; Json::Value fRoot; Json::Value& fResults; Json::Value* fBench; Json::Value* fConfig; }; /** * This ResultsWriter writes out to multiple ResultsWriters. */ class MultiResultsWriter : public ResultsWriter { public: MultiResultsWriter() : writers() { }; void add(ResultsWriter* writer) { writers.push_back(writer); } virtual void key(const char name[], const char value[]) { for (int i = 0; i < writers.count(); ++i) { writers[i]->key(name, value); } } virtual void option(const char name[], const char value[]) { for (int i = 0; i < writers.count(); ++i) { writers[i]->option(name, value); } } virtual void bench(const char name[], int32_t x, int32_t y) { for (int i = 0; i < writers.count(); ++i) { writers[i]->bench(name, x, y); } } virtual void config(const char name[]) { for (int i = 0; i < writers.count(); ++i) { writers[i]->config(name); } } virtual void configOption(const char name[], const char* value) { for (int i = 0; i < writers.count(); ++i) { writers[i]->configOption(name, value); } } virtual void timer(const char name[], double ms) { for (int i = 0; i < writers.count(); ++i) { writers[i]->timer(name, ms); } } virtual void end() { for (int i = 0; i < writers.count(); ++i) { writers[i]->end(); } } private: SkTArray writers; }; /** * Calls the end() method of T on destruction. */ template class CallEnd : SkNoncopyable { public: CallEnd(T& obj) : fObj(obj) {} ~CallEnd() { fObj.end(); } private: T& fObj; }; #endif