File: ResultAnalyzerMain.cpp

package info (click to toggle)
llvm-toolchain-15 1%3A15.0.6-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,554,644 kB
  • sloc: cpp: 5,922,452; ansic: 1,012,136; asm: 674,362; python: 191,568; objc: 73,855; f90: 42,327; lisp: 31,913; pascal: 11,973; javascript: 10,144; sh: 9,421; perl: 7,447; ml: 5,527; awk: 3,523; makefile: 2,520; xml: 885; cs: 573; fortran: 567
file content (175 lines) | stat: -rw-r--r-- 6,630 bytes parent folder | download | duplicates (9)
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); }