diff options
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/Analysis.cpp | 205 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/Analysis.h | 4 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/Clustering.h | 19 | ||||
-rw-r--r-- | llvm/unittests/tools/llvm-exegesis/ClusteringTest.cpp | 14 |
4 files changed, 197 insertions, 45 deletions
diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.cpp b/llvm/tools/llvm-exegesis/lib/Analysis.cpp index eac25a41116..cb3800e7db5 100644 --- a/llvm/tools/llvm-exegesis/lib/Analysis.cpp +++ b/llvm/tools/llvm-exegesis/lib/Analysis.cpp @@ -17,7 +17,15 @@ namespace exegesis { static const char kCsvSep = ','; -static void writeCsvEscaped(llvm::raw_ostream &OS, const std::string &S) { +namespace { + +enum EscapeTag { kEscapeCsv, kEscapeHtml }; + +template <EscapeTag Tag> +void writeEscaped(llvm::raw_ostream &OS, const llvm::StringRef S); + +template <> +void writeEscaped<kEscapeCsv>(llvm::raw_ostream &OS, const llvm::StringRef S) { if (std::find(S.begin(), S.end(), kCsvSep) == S.end()) { OS << S; } else { @@ -33,44 +41,68 @@ static void writeCsvEscaped(llvm::raw_ostream &OS, const std::string &S) { } } +template <> +void writeEscaped<kEscapeHtml>(llvm::raw_ostream &OS, const llvm::StringRef S) { + for (const char C : S) { + if (C == '<') + OS << "<"; + else if (C == '>') + OS << ">"; + else if (C == '&') + OS << "&"; + else + OS << C; + } +} + +} // namespace + +template <EscapeTag Tag> +static void +writeClusterId(llvm::raw_ostream &OS, + const InstructionBenchmarkClustering::ClusterId &CID) { + if (CID.isNoise()) + writeEscaped<Tag>(OS, "[noise]"); + else if (CID.isError()) + writeEscaped<Tag>(OS, "[error]"); + else + OS << CID.getId(); +} + +template <EscapeTag Tag> +static void writeMeasurementValue(llvm::raw_ostream &OS, const double Value) { + writeEscaped<Tag>(OS, llvm::formatv("{0:F}", Value).str()); +} + // Prints a row representing an instruction, along with scheduling info and // point coordinates (measurements). -void Analysis::printInstructionRow(const bool PrintSchedClass, - const size_t PointId, - llvm::raw_ostream &OS) const { +void Analysis::printInstructionRowCsv(const size_t PointId, + llvm::raw_ostream &OS) const { const InstructionBenchmark &Point = Clustering_.getPoints()[PointId]; - const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId); - if (ClusterId.isNoise()) - OS << "[noise]"; - else if (ClusterId.isError()) - OS << "[error]"; - else - OS << ClusterId.getId(); + writeClusterId<kEscapeCsv>(OS, Clustering_.getClusterIdForPoint(PointId)); OS << kCsvSep; - writeCsvEscaped(OS, Point.Key.OpcodeName); + writeEscaped<kEscapeCsv>(OS, Point.Key.OpcodeName); OS << kCsvSep; - writeCsvEscaped(OS, Point.Key.Config); - if (PrintSchedClass) { - OS << kCsvSep; - const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); - if (OpcodeIt != MnemonicToOpcode_.end()) { - const unsigned SchedClassId = - InstrInfo_->get(OpcodeIt->second).getSchedClass(); + writeEscaped<kEscapeCsv>(OS, Point.Key.Config); + OS << kCsvSep; + const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); + if (OpcodeIt != MnemonicToOpcode_.end()) { + const unsigned SchedClassId = + InstrInfo_->get(OpcodeIt->second).getSchedClass(); #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - const auto &SchedModel = SubtargetInfo_->getSchedModel(); - const llvm::MCSchedClassDesc *const SCDesc = - SchedModel.getSchedClassDesc(SchedClassId); - writeCsvEscaped(OS, SCDesc->Name); + const auto &SchedModel = SubtargetInfo_->getSchedModel(); + const llvm::MCSchedClassDesc *const SCDesc = + SchedModel.getSchedClassDesc(SchedClassId); + writeEscaped<kEscapeCsv>(OS, SCDesc->Name); #else - OS << SchedClassId; + OS << SchedClassId; #endif - } } // FIXME: Print the sched class once InstructionBenchmark separates key into // (mnemonic, mode, opaque). for (const auto &Measurement : Point.Measurements) { OS << kCsvSep; - writeCsvEscaped(OS, llvm::formatv("{0:F}", Measurement.Value)); + writeMeasurementValue<kEscapeCsv>(OS, Measurement.Value); } OS << "\n"; } @@ -102,7 +134,7 @@ Analysis::run<Analysis::PrintClusters>(llvm::raw_ostream &OS) const { << kCsvSep << "sched_class"; for (const auto &Measurement : Clustering_.getPoints().front().Measurements) { OS << kCsvSep; - writeCsvEscaped(OS, Measurement.Key); + writeEscaped<kEscapeCsv>(OS, Measurement.Key); } OS << "\n"; @@ -110,7 +142,7 @@ Analysis::run<Analysis::PrintClusters>(llvm::raw_ostream &OS) const { const auto &Clusters = Clustering_.getValidClusters(); for (size_t I = 0, E = Clusters.size(); I < E; ++I) { for (const size_t PointId : Clusters[I].PointIndices) { - printInstructionRow(/*PrintSchedClass*/ true, PointId, OS); + printInstructionRowCsv(PointId, OS); } OS << "\n\n"; } @@ -135,9 +167,110 @@ Analysis::makePointsPerSchedClass() const { return PointsPerSchedClass; } +void Analysis::printSchedClassHtml(std::vector<size_t> PointIds, + llvm::raw_ostream &OS) const { + assert(!PointIds.empty()); + // Sort the points by cluster id so that we can display them grouped by + // cluster. + std::sort(PointIds.begin(), PointIds.end(), + [this](const size_t A, const size_t B) { + return Clustering_.getClusterIdForPoint(A) < + Clustering_.getClusterIdForPoint(B); + }); + const auto &Points = Clustering_.getPoints(); + OS << "<table class=\"sched-class\">"; + OS << "<tr><th>ClusterId</th><th>Opcode/Config</th>"; + for (const auto &Measurement : Points[PointIds[0]].Measurements) { + OS << "<th>"; + writeEscaped<kEscapeHtml>(OS, Measurement.Key); + OS << "</th>"; + } + OS << "</tr>"; + for (size_t I = 0, E = PointIds.size(); I < E;) { + const auto &CurrentClusterId = + Clustering_.getClusterIdForPoint(PointIds[I]); + OS << "<tr><td>"; + writeClusterId<kEscapeHtml>(OS, CurrentClusterId); + OS << "</td><td><ul>"; + const auto &ClusterRepresentative = + Points[PointIds[I]]; // FIXME: average measurements. + for (; I < E && + Clustering_.getClusterIdForPoint(PointIds[I]) == CurrentClusterId; + ++I) { + OS << "<li><span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, Points[PointIds[I]].Key.OpcodeName); + OS << "</span> <span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, Points[PointIds[I]].Key.Config); + OS << "</span></li>"; + } + OS << "</ul></td>"; + for (const auto &Measurement : ClusterRepresentative.Measurements) { + OS << "<td>"; + writeMeasurementValue<kEscapeHtml>(OS, Measurement.Value); + OS << "</td>"; + } + OS << "</tr>"; + } + OS << "</table>"; +} + +static constexpr const char kHtmlHead[] = R"( +<head> +<title>llvm-exegesis Analysis Results</title> +<style> +body { + font-family: sans-serif +} +span.sched-class-name { + font-weight: bold; + font-family: monospace; +} +span.opcode { + font-family: monospace; +} +span.config { + font-family: monospace; +} +div.inconsistency { + margin-top: 50px; +} +table.sched-class { + margin-left: 50px; + border-collapse: collapse; +} +table.sched-class, table.sched-class tr,td,th { + border: 1px solid #444; +} +table.sched-class td { + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; + padding-bottom: 10px; +} +table.sched-class ul { + padding-left: 0px; + margin: 0px; + list-style-type: none; +} +span.mono { + font-family: monospace; +} +</style> +</head> +)"; + template <> llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>( llvm::raw_ostream &OS) const { + // Print the header. + OS << "<!DOCTYPE html><html>" << kHtmlHead << "<body>"; + OS << "<h1><span class=\"mono\">llvm-exegesis</span> Analysis Results</h1>"; + OS << "<h3>Triple: <span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, Clustering_.getPoints()[0].LLVMTriple); + OS << "</span></h3><h3>Cpu: <span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, Clustering_.getPoints()[0].CpuName); + OS << "</span></h3>"; + // All the points in a scheduling class should be in the same cluster. // Print any scheduling class for which this is not the case. for (const auto &SchedClassAndPoints : makePointsPerSchedClass()) { @@ -151,22 +284,24 @@ llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>( if (ClustersForSchedClass.size() <= 1) continue; // Nothing weird. - OS << "\nSched Class "; + OS << "<div class=\"inconsistency\"><p>Sched Class <span " + "class=\"sched-class-name\">"; #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) const auto &SchedModel = SubtargetInfo_->getSchedModel(); const llvm::MCSchedClassDesc *const SCDesc = SchedModel.getSchedClassDesc(SchedClassAndPoints.first); - OS << SCDesc->Name; + writeEscaped<kEscapeHtml>(OS, SCDesc->Name); #else OS << SchedClassAndPoints.first; #endif - OS << " contains instructions with distinct performance " + OS << "</span> contains instructions with distinct performance " "characteristics, falling into " - << ClustersForSchedClass.size() << " clusters:\n"; - for (const size_t PointId : SchedClassAndPoints.second) { - printInstructionRow(/*PrintSchedClass*/ false, PointId, OS); - } + << ClustersForSchedClass.size() << " clusters:</p>"; + printSchedClassHtml(SchedClassAndPoints.second, OS); + OS << "</div>"; } + + OS << "</body></html>"; return llvm::Error::success(); } diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.h b/llvm/tools/llvm-exegesis/lib/Analysis.h index d8046e54a70..ef9ded7ad3a 100644 --- a/llvm/tools/llvm-exegesis/lib/Analysis.h +++ b/llvm/tools/llvm-exegesis/lib/Analysis.h @@ -40,7 +40,9 @@ public: template <typename Pass> llvm::Error run(llvm::raw_ostream &OS) const; private: - void printInstructionRow(bool PrintSchedClass, size_t PointId, + void printInstructionRowCsv(size_t PointId, llvm::raw_ostream &OS) const; + + void printSchedClassHtml(std::vector<size_t> PointIds, llvm::raw_ostream &OS) const; // Builds a map of Sched Class -> indices of points that belong to the sched diff --git a/llvm/tools/llvm-exegesis/lib/Clustering.h b/llvm/tools/llvm-exegesis/lib/Clustering.h index 6b8f5a654d9..3d6413d2256 100644 --- a/llvm/tools/llvm-exegesis/lib/Clustering.h +++ b/llvm/tools/llvm-exegesis/lib/Clustering.h @@ -33,14 +33,14 @@ public: public: static ClusterId noise() { return ClusterId(kNoise); } static ClusterId error() { return ClusterId(kError); } - static ClusterId makeValid(int Id) { - assert(Id >= 0); + static ClusterId makeValid(size_t Id) { return ClusterId(Id); } ClusterId() : Id_(kUndef) {} bool operator==(const ClusterId &O) const { return Id_ == O.Id_; } + bool operator<(const ClusterId &O) const {return Id_ < O.Id_; } - bool isValid() const { return Id_ >= 0; } + bool isValid() const { return Id_ <= kMaxValid; } bool isUndef() const { return Id_ == kUndef; } bool isNoise() const { return Id_ == kNoise; } bool isError() const { return Id_ == kError; } @@ -48,15 +48,16 @@ public: // Precondition: isValid(). size_t getId() const { assert(isValid()); - return static_cast<size_t>(Id_); + return Id_; } private: - explicit ClusterId(int Id) : Id_(Id) {} - static constexpr const int kUndef = -1; - static constexpr const int kNoise = -2; - static constexpr const int kError = -3; - int Id_; + explicit ClusterId(size_t Id) : Id_(Id) {} + static constexpr const size_t kMaxValid = std::numeric_limits<size_t>::max() - 4; + static constexpr const size_t kNoise = kMaxValid + 1; + static constexpr const size_t kError = kMaxValid + 2; + static constexpr const size_t kUndef = kMaxValid + 3; + size_t Id_; }; struct Cluster { diff --git a/llvm/unittests/tools/llvm-exegesis/ClusteringTest.cpp b/llvm/unittests/tools/llvm-exegesis/ClusteringTest.cpp index b89c277b694..ecdb7d2a274 100644 --- a/llvm/unittests/tools/llvm-exegesis/ClusteringTest.cpp +++ b/llvm/unittests/tools/llvm-exegesis/ClusteringTest.cpp @@ -82,5 +82,19 @@ TEST(ClusteringTest, Clusters3D_InvalidOrder) { consumeError(std::move(Error)); } +TEST(ClusteringTest, Ordering) { + ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(1), + InstructionBenchmarkClustering::ClusterId::makeValid(2)); + + ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(2), + InstructionBenchmarkClustering::ClusterId::noise()); + + ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(2), + InstructionBenchmarkClustering::ClusterId::error()); + + ASSERT_LT(InstructionBenchmarkClustering::ClusterId::noise(), + InstructionBenchmarkClustering::ClusterId::error()); +} + } // namespace } // namespace exegesis |