Home | History | Annotate | Line # | Download | only in lib
      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