diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -65,7 +65,11 @@ class FunctionExecutor { public: virtual ~FunctionExecutor(); + // FIXME deprecate this. virtual Expected runAndMeasure(const char *Counters) const = 0; + + virtual Expected> + runAndMeasureMulti(const char *Counters) const = 0; }; protected: diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -46,9 +46,18 @@ private: Expected runAndMeasure(const char *Counters) const override { + auto resultOrError = runAndMeasureMulti(Counters); + if (resultOrError) + return resultOrError.get()[0]; + return resultOrError.takeError(); + } + + Expected> + runAndMeasureMulti(const char *Counters) const override { // We sum counts when there are several counters for a single ProcRes // (e.g. P23 on SandyBridge). - int64_t CounterValue = 0; + std::vector CounterValues; + CounterValues.reserve(16); // Counter returns at most 16 entries. SmallVector CounterNames; StringRef(Counters).split(CounterNames, '+'); char *const ScratchPtr = Scratch->ptr(); @@ -75,9 +84,20 @@ if (Crashed) return make_error("snippet crashed while running"); } - CounterValue += Counter->read(); + auto ValueOrError = Counter->readOrError(); + if (ValueOrError) { + int i = 0; + for (const int64_t &result : ValueOrError.get()) { + if (i >= CounterValues.size()) + CounterValues.push_back(result); + else + CounterValues[i] = CounterValues[i] + result; + ++i; + } + } else + return ValueOrError.takeError(); } - return CounterValue; + return CounterValues; } const LLVMState &State; diff --git a/llvm/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp --- a/llvm/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp +++ b/llvm/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp @@ -24,20 +24,49 @@ LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default; +static double squareStdev(const std::vector Values) { + if (Values.empty()) + return 0.0; + double Sum = 0; + for (const auto &V : Values) + Sum += V; + + const double Mean = Sum / Values.size(); + double Ret = 0; + for (const auto &V : Values) { + Ret += V - Mean; + } + return Ret / Values.size(); +} + Expected> LatencyBenchmarkRunner::runMeasurements( const FunctionExecutor &Executor) const { // Cycle measurements include some overhead from the kernel. Repeat the // measure several times and take the minimum value. constexpr const int NumMeasurements = 30; - int64_t MinValue = std::numeric_limits::max(); + std::vector WithMinStdev; + double Stdev = std::numeric_limits::max(); const char *CounterName = State.getPfmCounters().CycleCounter; for (size_t I = 0; I < NumMeasurements; ++I) { - auto ExpectedCounterValue = Executor.runAndMeasure(CounterName); + auto ExpectedCounterValue = Executor.runAndMeasureMulti(CounterName); if (!ExpectedCounterValue) return ExpectedCounterValue.takeError(); - if (*ExpectedCounterValue < MinValue) - MinValue = *ExpectedCounterValue; + + // We'll keep the reading with lowest stdev (ie., most stable) + double CurStdev = 0; + if (WithMinStdev.empty() || + Stdev < (CurStdev = squareStdev(*ExpectedCounterValue))) { + WithMinStdev = *ExpectedCounterValue; + Stdev = CurStdev; + } } + + int64_t MinValue = std::numeric_limits::max(); + for (const auto &V : WithMinStdev) { + if (V < MinValue) + MinValue = V; + } + std::vector Result; switch (Mode) { case InstructionBenchmark::Latency: diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.h b/llvm/tools/llvm-exegesis/lib/PerfHelper.h --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.h +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.h @@ -21,6 +21,7 @@ #include #include #include +#include struct perf_event_attr; @@ -85,7 +86,7 @@ int64_t read() const; /// Returns the current value of the counter or error if it cannot be read. - virtual llvm::Expected readOrError() const; + virtual llvm::Expected> readOrError() const; private: PerfEvent Event; diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp @@ -120,20 +120,20 @@ int64_t Counter::read() const { auto ValueOrError = readOrError(); if (ValueOrError) - return ValueOrError.get(); + return ValueOrError.get()[0]; errs() << ValueOrError.takeError() << "\n"; return -1; } -llvm::Expected Counter::readOrError() const { +llvm::Expected> Counter::readOrError() const { int64_t Count = 0; ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count)); if (ReadSize != sizeof(Count)) return llvm::make_error("Failed to read event counter", llvm::errc::io_error); - return Count; + return std::vector{Count}; } #else @@ -148,7 +148,7 @@ int64_t Counter::read() const { return 42; } -llvm::Expected Counter::readOrError() const { +llvm::Expected> Counter::readOrError() const { return llvm::make_error("Not implemented", llvm::errc::io_error); }