1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
//===-- 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
|