//===-- LatencyBenchmarkRunner.cpp ------------------------------*- C++ -*-===// // // 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 "LatencyBenchmarkRunner.h" #include "BenchmarkRunner.h" #include "Target.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" #include #include namespace llvm { namespace exegesis { LatencyBenchmarkRunner::LatencyBenchmarkRunner( const LLVMState &State, InstructionBenchmark::ModeE Mode, InstructionBenchmark::ResultAggregationModeE ResultAgg) : BenchmarkRunner(State, Mode) { assert((Mode == InstructionBenchmark::Latency || Mode == InstructionBenchmark::InverseThroughput) && "invalid mode"); ResultAggMode = ResultAgg; } LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default; static double computeVariance(const llvm::SmallVector &Values) { if (Values.empty()) return 0.0; double Sum = std::accumulate(Values.begin(), Values.end(), 0.0); const double Mean = Sum / Values.size(); double Ret = 0; for (const auto &V : Values) { double Delta = V - Mean; Ret += Delta * Delta; } return Ret / Values.size(); } static int64_t findMin(const llvm::SmallVector &Values) { if (Values.empty()) return 0; return *std::min_element(Values.begin(), Values.end()); } static int64_t findMax(const llvm::SmallVector &Values) { if (Values.empty()) return 0; return *std::max_element(Values.begin(), Values.end()); } static int64_t findMean(const llvm::SmallVector &Values) { if (Values.empty()) return 0; return std::accumulate(Values.begin(), Values.end(), 0.0) / static_cast(Values.size()); } Expected> LatencyBenchmarkRunner::runMeasurements( const FunctionExecutor &Executor) const { // Cycle measurements include some overhead from the kernel. Repeat the // measure several times and return the aggregated value, as specified by // ResultAggMode. constexpr const int NumMeasurements = 30; llvm::SmallVector AccumulatedValues; double MinVariance = std::numeric_limits::infinity(); const char *CounterName = State.getPfmCounters().CycleCounter; // Values count for each run. int ValuesCount = 0; for (size_t I = 0; I < NumMeasurements; ++I) { auto ExpectedCounterValues = Executor.runAndSample(CounterName); if (!ExpectedCounterValues) return ExpectedCounterValues.takeError(); ValuesCount = ExpectedCounterValues.get().size(); if (ValuesCount == 1) AccumulatedValues.push_back(ExpectedCounterValues.get()[0]); else { // We'll keep the reading with lowest variance (ie., most stable) double Variance = computeVariance(*ExpectedCounterValues); if (MinVariance > Variance) { AccumulatedValues = std::move(ExpectedCounterValues.get()); MinVariance = Variance; } } } std::string ModeName; switch (Mode) { case InstructionBenchmark::Latency: ModeName = "latency"; break; case InstructionBenchmark::InverseThroughput: ModeName = "inverse_throughput"; break; default: break; } switch (ResultAggMode) { case InstructionBenchmark::MinVariance: { if (ValuesCount == 1) llvm::errs() << "Each sample only has one value. result-aggregation-mode " "of min-variance is probably non-sensical\n"; std::vector Result; Result.reserve(AccumulatedValues.size()); for (const int64_t Value : AccumulatedValues) Result.push_back(BenchmarkMeasure::Create(ModeName, Value)); return std::move(Result); } case InstructionBenchmark::Min: { std::vector Result; Result.push_back( BenchmarkMeasure::Create(ModeName, findMin(AccumulatedValues))); return std::move(Result); } case InstructionBenchmark::Max: { std::vector Result; Result.push_back( BenchmarkMeasure::Create(ModeName, findMax(AccumulatedValues))); return std::move(Result); } case InstructionBenchmark::Mean: { std::vector Result; Result.push_back( BenchmarkMeasure::Create(ModeName, findMean(AccumulatedValues))); return std::move(Result); } } return llvm::make_error(llvm::Twine("Unexpected benchmark mode(") .concat(std::to_string(Mode)) .concat(" and unexpected ResultAggMode ") .concat(std::to_string(ResultAggMode))); } } // namespace exegesis } // namespace llvm