[llvm-exegesis] Analysis: detect clustering inconsistencies.
Summary:
Warn on instructions that should have the same performance
characteristics according to the sched model but actually
differ in their benchmarks.
Next step: Make the display nicer to browse, I was thinking maybe html.
Reviewers: gchatelet
Subscribers: tschuett, llvm-commits
Differential Revision: https://reviews.llvm.org/D46945
llvm-svn: 332601
diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.cpp b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
index e026308..5b60925 100644
--- a/llvm/tools/llvm-exegesis/lib/Analysis.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
@@ -10,6 +10,7 @@
#include "Analysis.h"
#include "BenchmarkResult.h"
#include "llvm/Support/FormatVariadic.h"
+#include <unordered_set>
#include <vector>
namespace exegesis {
@@ -34,26 +35,36 @@
// Prints a row representing an instruction, along with scheduling info and
// point coordinates (measurements).
-void Analysis::printInstructionRow(const size_t ClusterId, const size_t PointId,
+void Analysis::printInstructionRow(const bool PrintSchedClass,
+ const size_t PointId,
llvm::raw_ostream &OS) const {
const InstructionBenchmark &Point = Clustering_.getPoints()[PointId];
-
- OS << ClusterId << kCsvSep;
+ const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId);
+ if (ClusterId.isNoise())
+ OS << "[noise]";
+ else if (ClusterId.isError())
+ OS << "[error]";
+ else
+ OS << ClusterId.getId();
+ OS << kCsvSep;
writeCsvEscaped(OS, Point.Key.OpcodeName);
OS << kCsvSep;
writeCsvEscaped(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 (PrintSchedClass) {
+ 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);
+ writeCsvEscaped(OS, SCDesc->Name);
#else
- OS << SchedClassId;
+ OS << SchedClassId;
#endif
+ }
}
// FIXME: Print the sched class once InstructionBenchmark separates key into
// (mnemonic, mode, opaque).
@@ -72,8 +83,8 @@
InstrInfo_.reset(Target.createMCInstrInfo());
const InstructionBenchmark &FirstPoint = Clustering.getPoints().front();
- SubtargetInfo_.reset(Target.createMCSubtargetInfo(
- FirstPoint.LLVMTriple, FirstPoint.CpuName, ""));
+ SubtargetInfo_.reset(Target.createMCSubtargetInfo(FirstPoint.LLVMTriple,
+ FirstPoint.CpuName, ""));
// Build an index of mnemonic->opcode.
for (int I = 0, E = InstrInfo_->getNumOpcodes(); I < E; ++I)
@@ -94,14 +105,66 @@
OS << "\n";
// Write the points.
- const auto& Clusters = Clustering_.getValidClusters();
+ 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(I, PointId, OS);
+ printInstructionRow(/*PrintSchedClass*/ true, PointId, OS);
}
OS << "\n\n";
}
return llvm::Error::success();
}
+std::unordered_map<unsigned, std::vector<size_t>>
+Analysis::makePointsPerSchedClass() const {
+ std::unordered_map<unsigned, std::vector<size_t>> PointsPerSchedClass;
+ const auto &Points = Clustering_.getPoints();
+ for (size_t PointId = 0, E = Points.size(); PointId < E; ++PointId) {
+ const InstructionBenchmark &Point = Points[PointId];
+ if (!Point.Error.empty())
+ continue;
+ const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName);
+ if (OpcodeIt == MnemonicToOpcode_.end())
+ continue;
+ const unsigned SchedClassId =
+ InstrInfo_->get(OpcodeIt->second).getSchedClass();
+ PointsPerSchedClass[SchedClassId].push_back(PointId);
+ }
+ return PointsPerSchedClass;
+}
+
+llvm::Error
+Analysis::printSchedClassInconsistencies(llvm::raw_ostream &OS) const {
+ // 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()) {
+ std::unordered_set<size_t> ClustersForSchedClass;
+ for (const size_t PointId : SchedClassAndPoints.second) {
+ const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId);
+ if (!ClusterId.isValid())
+ continue; // Ignore noise and errors.
+ ClustersForSchedClass.insert(ClusterId.getId());
+ }
+ if (ClustersForSchedClass.size() <= 1)
+ continue; // Nothing weird.
+
+ OS << "\nSched Class ";
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ const auto &SchedModel = SubtargetInfo_->getSchedModel();
+ const llvm::MCSchedClassDesc *const SCDesc =
+ SchedModel.getSchedClassDesc(SchedClassAndPoints.first);
+ OS << SCDesc->Name;
+#else
+ OS << SchedClassAndPoints.first;
+#endif
+ OS << " contains instructions with distinct performance "
+ "characteristics, falling into "
+ << ClustersForSchedClass.size() << " clusters:\n";
+ for (const size_t PointId : SchedClassAndPoints.second) {
+ printInstructionRow(/*PrintSchedClass*/ false, PointId, OS);
+ }
+ }
+ return llvm::Error::success();
+}
+
} // namespace exegesis