1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
//===-- Application to analyze benchmark JSON files -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "automemcpy/ResultAnalyzer.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/MemoryBuffer.h"
namespace llvm {
// User can specify one or more json filenames to process on the command line.
static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
cl::desc("<input json files>"));
namespace automemcpy {
// This is defined in the autogenerated 'Implementations.cpp' file.
extern ArrayRef<NamedFunctionDescriptor> getFunctionDescriptors();
// Iterates over all functions and fills a map of function name to function
// descriptor pointers.
static StringMap<const FunctionDescriptor *> createFunctionDescriptorMap() {
StringMap<const FunctionDescriptor *> Descriptors;
for (const NamedFunctionDescriptor &FD : getFunctionDescriptors())
Descriptors.insert_or_assign(FD.Name, &FD.Desc);
return Descriptors;
}
// Retrieves the function descriptor for a particular function name.
static const FunctionDescriptor &getFunctionDescriptor(StringRef FunctionName) {
static StringMap<const FunctionDescriptor *> Descriptors =
createFunctionDescriptorMap();
const auto *FD = Descriptors.lookup(FunctionName);
if (!FD)
report_fatal_error(
Twine("No FunctionDescriptor for ").concat(FunctionName));
return *FD;
}
// Functions and distributions names are stored quite a few times so it's more
// efficient to internalize these strings and refer to them through 'StringRef'.
static StringRef getInternalizedString(StringRef VolatileStr) {
static llvm::StringSet<> StringCache;
return StringCache.insert(VolatileStr).first->getKey();
}
// Helper function for the LLVM JSON API.
bool fromJSON(const json::Value &V, Sample &Out, json::Path P) {
std::string Label;
json::ObjectMapper O(V, P);
if (O && O.map("bytes_per_second", Out.BytesPerSecond) &&
O.map("label", Label)) {
const auto LabelPair = StringRef(Label).split(',');
Out.Id.Function.Name = getInternalizedString(LabelPair.first);
Out.Id.Function.Type = getFunctionDescriptor(LabelPair.first).Type;
Out.Id.Distribution.Name = getInternalizedString(LabelPair.second);
return true;
}
return false;
}
// An object to represent the content of the JSON file.
// This is easier to parse/serialize JSON when the structures of the json file
// maps the structure of the object.
struct JsonFile {
std::vector<Sample> Samples;
};
// Helper function for the LLVM JSON API.
bool fromJSON(const json::Value &V, JsonFile &JF, json::Path P) {
json::ObjectMapper O(V, P);
return O && O.map("benchmarks", JF.Samples);
}
// Global object to ease error reporting, it consumes errors and crash the
// application with a meaningful message.
static ExitOnError ExitOnErr;
// Main JSON parsing method. Reads the content of the file pointed to by
// 'Filename' and returns a JsonFile object.
JsonFile parseJsonResultFile(StringRef Filename) {
auto Buf = ExitOnErr(errorOrToExpected(
MemoryBuffer::getFile(Filename, /*bool IsText=*/true,
/*RequiresNullTerminator=*/false)));
auto JsonValue = ExitOnErr(json::parse(Buf->getBuffer()));
json::Path::Root Root;
JsonFile JF;
if (!fromJSON(JsonValue, JF, Root))
ExitOnErr(Root.get_error());
return JF;
}
// Serializes the 'GradeHisto' to the provided 'Stream'.
static void Serialize(raw_ostream &Stream, const GradeHistogram &GH) {
static constexpr std::array<StringRef, 9> kCharacters = {
" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};
const size_t Max = *std::max_element(GH.begin(), GH.end());
for (size_t I = 0; I < GH.size(); ++I) {
size_t Index = (float(GH[I]) / Max) * (kCharacters.size() - 1);
Stream << kCharacters.at(Index);
}
}
int Main(int argc, char **argv) {
ExitOnErr.setBanner("Automemcpy Json Results Analyzer stopped with error: ");
cl::ParseCommandLineOptions(argc, argv, "Automemcpy Json Results Analyzer\n");
// Reads all samples stored in the input JSON files.
std::vector<Sample> Samples;
for (const auto &Filename : InputFilenames) {
auto Result = parseJsonResultFile(Filename);
llvm::append_range(Samples, Result.Samples);
}
// Extracts median of throughputs.
std::vector<FunctionData> Functions = getThroughputs(Samples);
fillScores(Functions);
castVotes(Functions);
// TODO: Implement tie breaking algorithm.
std::sort(Functions.begin(), Functions.end(),
[](const FunctionData &A, const FunctionData &B) {
return A.FinalGrade < B.FinalGrade;
});
// Present data by function type.
std::stable_sort(Functions.begin(), Functions.end(),
[](const FunctionData &A, const FunctionData &B) {
return A.Id.Type < B.Id.Type;
});
// Print result.
for (const FunctionData &Function : Functions) {
outs() << formatv("{0,-10}", Grade::getString(Function.FinalGrade));
outs() << " |";
Serialize(outs(), Function.GradeHisto);
outs() << "| ";
outs().resetColor();
outs() << formatv("{0,+25}", Function.Id.Name);
outs() << "\n";
}
return EXIT_SUCCESS;
}
} // namespace automemcpy
} // namespace llvm
int main(int argc, char **argv) { return llvm::automemcpy::Main(argc, argv); }
|