1 //===-- LatencyBenchmarkRunner.cpp ------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "LatencyBenchmarkRunner.h" 10 11 #include "BenchmarkRunner.h" 12 #include "Target.h" 13 #include "llvm/ADT/Twine.h" 14 #include "llvm/Support/Error.h" 15 #include <algorithm> 16 #include <cmath> 17 18 namespace llvm { 19 namespace exegesis { 20 21 LatencyBenchmarkRunner::LatencyBenchmarkRunner( 22 const LLVMState &State, InstructionBenchmark::ModeE Mode, 23 InstructionBenchmark::ResultAggregationModeE ResultAgg) 24 : BenchmarkRunner(State, Mode) { 25 assert((Mode == InstructionBenchmark::Latency || 26 Mode == InstructionBenchmark::InverseThroughput) && 27 "invalid mode"); 28 ResultAggMode = ResultAgg; 29 } 30 31 LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default; 32 33 static double computeVariance(const llvm::SmallVector<int64_t, 4> &Values) { 34 if (Values.empty()) 35 return 0.0; 36 double Sum = std::accumulate(Values.begin(), Values.end(), 0.0); 37 38 const double Mean = Sum / Values.size(); 39 double Ret = 0; 40 for (const auto &V : Values) { 41 double Delta = V - Mean; 42 Ret += Delta * Delta; 43 } 44 return Ret / Values.size(); 45 } 46 47 static int64_t findMin(const llvm::SmallVector<int64_t, 4> &Values) { 48 if (Values.empty()) 49 return 0; 50 return *std::min_element(Values.begin(), Values.end()); 51 } 52 53 static int64_t findMax(const llvm::SmallVector<int64_t, 4> &Values) { 54 if (Values.empty()) 55 return 0; 56 return *std::max_element(Values.begin(), Values.end()); 57 } 58 59 static int64_t findMean(const llvm::SmallVector<int64_t, 4> &Values) { 60 if (Values.empty()) 61 return 0; 62 return std::accumulate(Values.begin(), Values.end(), 0.0) / 63 static_cast<double>(Values.size()); 64 } 65 66 Expected<std::vector<BenchmarkMeasure>> LatencyBenchmarkRunner::runMeasurements( 67 const FunctionExecutor &Executor) const { 68 // Cycle measurements include some overhead from the kernel. Repeat the 69 // measure several times and return the aggregated value, as specified by 70 // ResultAggMode. 71 constexpr const int NumMeasurements = 30; 72 llvm::SmallVector<int64_t, 4> AccumulatedValues; 73 double MinVariance = std::numeric_limits<double>::infinity(); 74 const char *CounterName = State.getPfmCounters().CycleCounter; 75 // Values count for each run. 76 int ValuesCount = 0; 77 for (size_t I = 0; I < NumMeasurements; ++I) { 78 auto ExpectedCounterValues = Executor.runAndSample(CounterName); 79 if (!ExpectedCounterValues) 80 return ExpectedCounterValues.takeError(); 81 ValuesCount = ExpectedCounterValues.get().size(); 82 if (ValuesCount == 1) 83 AccumulatedValues.push_back(ExpectedCounterValues.get()[0]); 84 else { 85 // We'll keep the reading with lowest variance (ie., most stable) 86 double Variance = computeVariance(*ExpectedCounterValues); 87 if (MinVariance > Variance) { 88 AccumulatedValues = std::move(ExpectedCounterValues.get()); 89 MinVariance = Variance; 90 } 91 } 92 } 93 94 std::string ModeName; 95 switch (Mode) { 96 case InstructionBenchmark::Latency: 97 ModeName = "latency"; 98 break; 99 case InstructionBenchmark::InverseThroughput: 100 ModeName = "inverse_throughput"; 101 break; 102 default: 103 break; 104 } 105 106 switch (ResultAggMode) { 107 case InstructionBenchmark::MinVariance: { 108 if (ValuesCount == 1) 109 llvm::errs() << "Each sample only has one value. result-aggregation-mode " 110 "of min-variance is probably non-sensical\n"; 111 std::vector<BenchmarkMeasure> Result; 112 Result.reserve(AccumulatedValues.size()); 113 for (const int64_t Value : AccumulatedValues) 114 Result.push_back(BenchmarkMeasure::Create(ModeName, Value)); 115 return std::move(Result); 116 } 117 case InstructionBenchmark::Min: { 118 std::vector<BenchmarkMeasure> Result; 119 Result.push_back( 120 BenchmarkMeasure::Create(ModeName, findMin(AccumulatedValues))); 121 return std::move(Result); 122 } 123 case InstructionBenchmark::Max: { 124 std::vector<BenchmarkMeasure> Result; 125 Result.push_back( 126 BenchmarkMeasure::Create(ModeName, findMax(AccumulatedValues))); 127 return std::move(Result); 128 } 129 case InstructionBenchmark::Mean: { 130 std::vector<BenchmarkMeasure> Result; 131 Result.push_back( 132 BenchmarkMeasure::Create(ModeName, findMean(AccumulatedValues))); 133 return std::move(Result); 134 } 135 } 136 return llvm::make_error<Failure>(llvm::Twine("Unexpected benchmark mode(") 137 .concat(std::to_string(Mode)) 138 .concat(" and unexpected ResultAggMode ") 139 .concat(std::to_string(ResultAggMode))); 140 } 141 142 } // namespace exegesis 143 } // namespace llvm 144