| //===-- 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 <algorithm> |
| #include <cmath> |
| |
| 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<int64_t, 4> &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<int64_t, 4> &Values) { |
| if (Values.empty()) |
| return 0; |
| return *std::min_element(Values.begin(), Values.end()); |
| } |
| |
| static int64_t findMax(const llvm::SmallVector<int64_t, 4> &Values) { |
| if (Values.empty()) |
| return 0; |
| return *std::max_element(Values.begin(), Values.end()); |
| } |
| |
| static int64_t findMean(const llvm::SmallVector<int64_t, 4> &Values) { |
| if (Values.empty()) |
| return 0; |
| return std::accumulate(Values.begin(), Values.end(), 0.0) / |
| static_cast<double>(Values.size()); |
| } |
| |
| Expected<std::vector<BenchmarkMeasure>> 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<int64_t, 4> AccumulatedValues; |
| double MinVariance = std::numeric_limits<double>::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<BenchmarkMeasure> 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<BenchmarkMeasure> Result; |
| Result.push_back( |
| BenchmarkMeasure::Create(ModeName, findMin(AccumulatedValues))); |
| return std::move(Result); |
| } |
| case InstructionBenchmark::Max: { |
| std::vector<BenchmarkMeasure> Result; |
| Result.push_back( |
| BenchmarkMeasure::Create(ModeName, findMax(AccumulatedValues))); |
| return std::move(Result); |
| } |
| case InstructionBenchmark::Mean: { |
| std::vector<BenchmarkMeasure> Result; |
| Result.push_back( |
| BenchmarkMeasure::Create(ModeName, findMean(AccumulatedValues))); |
| return std::move(Result); |
| } |
| } |
| return llvm::make_error<Failure>(llvm::Twine("Unexpected benchmark mode(") |
| .concat(std::to_string(Mode)) |
| .concat(" and unexpected ResultAggMode ") |
| .concat(std::to_string(ResultAggMode))); |
| } |
| |
| } // namespace exegesis |
| } // namespace llvm |