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