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