289 lines · cpp
1//===-- Benchmark ---------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "JSON.h"10#include "LibcBenchmark.h"11#include "LibcMemoryBenchmark.h"12#include "MemorySizeDistributions.h"13#include "src/__support/macros/config.h"14#include "llvm/Support/CommandLine.h"15#include "llvm/Support/ErrorHandling.h"16#include "llvm/Support/FileSystem.h"17#include "llvm/Support/JSON.h"18#include "llvm/Support/MathExtras.h"19#include "llvm/Support/MemoryBuffer.h"20#include "llvm/Support/raw_ostream.h"21 22#include <cstring>23#include <unistd.h>24 25namespace LIBC_NAMESPACE_DECL {26 27extern void *memcpy(void *__restrict, const void *__restrict, size_t);28extern void *memmove(void *, const void *, size_t);29extern void *memset(void *, int, size_t);30extern void bzero(void *, size_t);31extern int memcmp(const void *, const void *, size_t);32extern int bcmp(const void *, const void *, size_t);33 34} // namespace LIBC_NAMESPACE_DECL35 36namespace llvm {37namespace libc_benchmarks {38 39static cl::opt<std::string>40 StudyName("study-name", cl::desc("The name for this study"), cl::Required);41 42static cl::opt<std::string>43 SizeDistributionName("size-distribution-name",44 cl::desc("The name of the distribution to use"));45 46static cl::opt<bool> SweepMode(47 "sweep-mode",48 cl::desc(49 "If set, benchmark all sizes from sweep-min-size to sweep-max-size"));50 51static cl::opt<uint32_t>52 SweepMinSize("sweep-min-size",53 cl::desc("The minimum size to use in sweep-mode"),54 cl::init(0));55 56static cl::opt<uint32_t>57 SweepMaxSize("sweep-max-size",58 cl::desc("The maximum size to use in sweep-mode"),59 cl::init(256));60 61static cl::opt<uint32_t>62 AlignedAccess("aligned-access",63 cl::desc("The alignment to use when accessing the buffers\n"64 "Default is unaligned\n"65 "Use 0 to disable address randomization"),66 cl::init(1));67 68static cl::opt<std::string> Output("output",69 cl::desc("Specify output filename"),70 cl::value_desc("filename"), cl::init("-"));71 72static cl::opt<uint32_t>73 NumTrials("num-trials", cl::desc("The number of benchmarks run to perform"),74 cl::init(1));75 76#if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY)77#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCPY78using BenchmarkSetup = CopySetup;79#elif defined(LIBC_BENCHMARK_FUNCTION_MEMMOVE)80#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMMOVE81using BenchmarkSetup = MoveSetup;82#elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET)83#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMSET84using BenchmarkSetup = SetSetup;85#elif defined(LIBC_BENCHMARK_FUNCTION_BZERO)86#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BZERO87using BenchmarkSetup = SetSetup;88#elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP)89#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP90using BenchmarkSetup = ComparisonSetup;91#elif defined(LIBC_BENCHMARK_FUNCTION_BCMP)92#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BCMP93using BenchmarkSetup = ComparisonSetup;94#else95#error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition"96#endif97 98struct MemfunctionBenchmarkBase : public BenchmarkSetup {99 MemfunctionBenchmarkBase() : ReportProgress(isatty(fileno(stdout))) {}100 virtual ~MemfunctionBenchmarkBase() {}101 102 virtual Study run() = 0;103 104 CircularArrayRef<ParameterBatch::ParameterType>105 generateBatch(size_t Iterations) {106 randomize();107 return cycle(ArrayRef(Parameters), Iterations);108 }109 110protected:111 Study createStudy() {112 Study Study;113 // Setup study.114 Study.StudyName = StudyName;115 Runtime &RI = Study.Runtime;116 RI.Host = HostState::get();117 RI.BufferSize = BufferSize;118 RI.BatchParameterCount = BatchSize;119 120 BenchmarkOptions &BO = RI.BenchmarkOptions;121 BO.MinDuration = std::chrono::milliseconds(1);122 BO.MaxDuration = std::chrono::seconds(1);123 BO.MaxIterations = 10'000'000U;124 BO.MinSamples = 4;125 BO.MaxSamples = 1000;126 BO.Epsilon = 0.01; // 1%127 BO.ScalingFactor = 1.4;128 129 StudyConfiguration &SC = Study.Configuration;130 SC.NumTrials = NumTrials;131 SC.IsSweepMode = SweepMode;132 SC.AccessAlignment = MaybeAlign(AlignedAccess);133 SC.Function = LIBC_BENCHMARK_FUNCTION_NAME;134 return Study;135 }136 137 void runTrials(const BenchmarkOptions &Options,138 std::vector<Duration> &Measurements) {139 for (size_t i = 0; i < NumTrials; ++i) {140 const BenchmarkResult Result = benchmark(141 Options, *this, [this](ParameterBatch::ParameterType Parameter) {142 return Call(Parameter, LIBC_BENCHMARK_FUNCTION);143 });144 Measurements.push_back(Result.BestGuess);145 reportProgress(Measurements);146 }147 }148 149 virtual void randomize() = 0;150 151private:152 bool ReportProgress;153 154 void reportProgress(const std::vector<Duration> &Measurements) {155 if (!ReportProgress)156 return;157 static size_t LastPercent = -1;158 const size_t TotalSteps = Measurements.capacity();159 const size_t Steps = Measurements.size();160 const size_t Percent = 100 * Steps / TotalSteps;161 if (Percent == LastPercent)162 return;163 LastPercent = Percent;164 size_t i = 0;165 errs() << '[';166 for (; i <= Percent; ++i)167 errs() << '#';168 for (; i <= 100; ++i)169 errs() << '_';170 errs() << "] " << Percent << '%' << '\r';171 }172};173 174struct MemfunctionBenchmarkSweep final : public MemfunctionBenchmarkBase {175 MemfunctionBenchmarkSweep()176 : OffsetSampler(MemfunctionBenchmarkBase::BufferSize, SweepMaxSize,177 MaybeAlign(AlignedAccess)) {}178 179 virtual void randomize() override {180 for (auto &P : Parameters) {181 P.OffsetBytes = OffsetSampler(Gen);182 P.SizeBytes = CurrentSweepSize;183 checkValid(P);184 }185 }186 187 virtual Study run() override {188 Study Study = createStudy();189 Study.Configuration.SweepModeMaxSize = SweepMaxSize;190 BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions;191 BO.MinDuration = std::chrono::milliseconds(1);192 BO.InitialIterations = 100;193 auto &Measurements = Study.Measurements;194 Measurements.reserve(NumTrials * SweepMaxSize);195 for (size_t Size = SweepMinSize; Size <= SweepMaxSize; ++Size) {196 CurrentSweepSize = Size;197 runTrials(BO, Measurements);198 }199 return Study;200 }201 202private:203 size_t CurrentSweepSize = 0;204 OffsetDistribution OffsetSampler;205 std::mt19937_64 Gen;206};207 208struct MemfunctionBenchmarkDistribution final209 : public MemfunctionBenchmarkBase {210 MemfunctionBenchmarkDistribution(MemorySizeDistribution Distribution)211 : Distribution(Distribution), Probabilities(Distribution.Probabilities),212 SizeSampler(Probabilities.begin(), Probabilities.end()),213 OffsetSampler(MemfunctionBenchmarkBase::BufferSize,214 Probabilities.size() - 1, MaybeAlign(AlignedAccess)) {}215 216 virtual void randomize() override {217 for (auto &P : Parameters) {218 P.OffsetBytes = OffsetSampler(Gen);219 P.SizeBytes = SizeSampler(Gen);220 checkValid(P);221 }222 }223 224 virtual Study run() override {225 Study Study = createStudy();226 Study.Configuration.SizeDistributionName = Distribution.Name.str();227 BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions;228 BO.MinDuration = std::chrono::milliseconds(10);229 BO.InitialIterations = BatchSize * 10;230 auto &Measurements = Study.Measurements;231 Measurements.reserve(NumTrials);232 runTrials(BO, Measurements);233 return Study;234 }235 236private:237 MemorySizeDistribution Distribution;238 ArrayRef<double> Probabilities;239 std::discrete_distribution<unsigned> SizeSampler;240 OffsetDistribution OffsetSampler;241 std::mt19937_64 Gen;242};243 244void writeStudy(const Study &S) {245 std::error_code EC;246 raw_fd_ostream FOS(Output, EC);247 if (EC)248 report_fatal_error(Twine("Could not open file: ")249 .concat(EC.message())250 .concat(", ")251 .concat(Output));252 json::OStream JOS(FOS);253 serializeToJson(S, JOS);254 FOS << "\n";255}256 257void main() {258 checkRequirements();259 if (!isPowerOf2_32(AlignedAccess))260 report_fatal_error(AlignedAccess.ArgStr +261 Twine(" must be a power of two or zero"));262 263 const bool HasDistributionName = !SizeDistributionName.empty();264 if (SweepMode && HasDistributionName)265 report_fatal_error("Select only one of `--" + Twine(SweepMode.ArgStr) +266 "` or `--" + Twine(SizeDistributionName.ArgStr) + "`");267 268 std::unique_ptr<MemfunctionBenchmarkBase> Benchmark;269 if (SweepMode)270 Benchmark.reset(new MemfunctionBenchmarkSweep());271 else272 Benchmark.reset(new MemfunctionBenchmarkDistribution(getDistributionOrDie(273 BenchmarkSetup::getDistributions(), SizeDistributionName)));274 writeStudy(Benchmark->run());275}276 277} // namespace libc_benchmarks278} // namespace llvm279 280#ifndef NDEBUG281#error For reproducibility benchmarks should not be compiled in DEBUG mode.282#endif283 284int main(int argc, char **argv) {285 llvm::cl::ParseCommandLineOptions(argc, argv);286 llvm::libc_benchmarks::main();287 return EXIT_SUCCESS;288}289