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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
//===-- 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>"));
// User can filter the distributions to be taken into account.
static cl::list<std::string>
KeepOnlyDistributions("keep-only-distributions",
cl::desc("<comma separated list of distribution "
"names, keeps all if unspecified>"));
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;
std::string RunType;
json::ObjectMapper O(V, P);
if (O && O.map("bytes_per_second", Out.BytesPerSecond) &&
O.map("run_type", RunType) && 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);
Out.Type = StringSwitch<SampleType>(RunType)
.Case("aggregate", SampleType::AGGREGATE)
.Case("iteration", SampleType::ITERATION);
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.getError());
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);
}
if (!KeepOnlyDistributions.empty()) {
llvm::StringSet ValidDistributions;
ValidDistributions.insert(KeepOnlyDistributions.begin(),
KeepOnlyDistributions.end());
llvm::erase_if(Samples, [&ValidDistributions](const Sample &S) {
return !ValidDistributions.contains(S.Id.Distribution.Name);
});
}
// Extracts median of throughputs.
std::vector<FunctionData> Functions = getThroughputs(Samples);
fillScores(Functions);
castVotes(Functions);
// Present data by function type, Grade and Geomean of scores.
std::sort(Functions.begin(), Functions.end(),
[](const FunctionData &A, const FunctionData &B) {
const auto Less = [](const FunctionData &FD) {
return std::make_tuple(FD.Id.Type, FD.FinalGrade,
-FD.ScoresGeoMean);
};
return Less(A) < Less(B);
});
// 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); }
|