//===-- 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