1438f7fc0SSiva Chandra Reddy //===-- Benchmark ---------------------------------------------------------===// 2438f7fc0SSiva Chandra Reddy // 3438f7fc0SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4438f7fc0SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information. 5438f7fc0SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6438f7fc0SSiva Chandra Reddy // 7438f7fc0SSiva Chandra Reddy //===----------------------------------------------------------------------===// 8438f7fc0SSiva Chandra Reddy 9438f7fc0SSiva Chandra Reddy #include "JSON.h" 10438f7fc0SSiva Chandra Reddy #include "LibcBenchmark.h" 11438f7fc0SSiva Chandra Reddy #include "LibcMemoryBenchmark.h" 12deae7e98SGuillaume Chatelet #include "MemorySizeDistributions.h" 13438f7fc0SSiva Chandra Reddy #include "llvm/Support/CommandLine.h" 14438f7fc0SSiva Chandra Reddy #include "llvm/Support/ErrorHandling.h" 15438f7fc0SSiva Chandra Reddy #include "llvm/Support/FileSystem.h" 16438f7fc0SSiva Chandra Reddy #include "llvm/Support/JSON.h" 17d3c70d9fSGuillaume Chatelet #include "llvm/Support/MathExtras.h" 18438f7fc0SSiva Chandra Reddy #include "llvm/Support/MemoryBuffer.h" 19438f7fc0SSiva Chandra Reddy #include "llvm/Support/raw_ostream.h" 20438f7fc0SSiva Chandra Reddy 2187065c0dSGuillaume Chatelet #include <cstring> 22d3c70d9fSGuillaume Chatelet #include <unistd.h> 2387065c0dSGuillaume Chatelet 24deae7e98SGuillaume Chatelet namespace __llvm_libc { 25deae7e98SGuillaume Chatelet 26deae7e98SGuillaume Chatelet extern void *memcpy(void *__restrict, const void *__restrict, size_t); 27deae7e98SGuillaume Chatelet extern void *memset(void *, int, size_t); 2887065c0dSGuillaume Chatelet extern void bzero(void *, size_t); 2987065c0dSGuillaume Chatelet extern int memcmp(const void *, const void *, size_t); 30*c8f79892SGuillaume Chatelet extern int bcmp(const void *, const void *, size_t); 31deae7e98SGuillaume Chatelet 32deae7e98SGuillaume Chatelet } // namespace __llvm_libc 33438f7fc0SSiva Chandra Reddy 34438f7fc0SSiva Chandra Reddy namespace llvm { 35438f7fc0SSiva Chandra Reddy namespace libc_benchmarks { 36438f7fc0SSiva Chandra Reddy 37deae7e98SGuillaume Chatelet static cl::opt<std::string> 38deae7e98SGuillaume Chatelet StudyName("study-name", cl::desc("The name for this study"), cl::Required); 39deae7e98SGuillaume Chatelet 40deae7e98SGuillaume Chatelet static cl::opt<std::string> 41deae7e98SGuillaume Chatelet SizeDistributionName("size-distribution-name", 42deae7e98SGuillaume Chatelet cl::desc("The name of the distribution to use")); 43deae7e98SGuillaume Chatelet 44deae7e98SGuillaume Chatelet static cl::opt<bool> 45deae7e98SGuillaume Chatelet SweepMode("sweep-mode", 46deae7e98SGuillaume Chatelet cl::desc("If set, benchmark all sizes from 0 to sweep-max-size")); 47deae7e98SGuillaume Chatelet 48deae7e98SGuillaume Chatelet static cl::opt<uint32_t> 49deae7e98SGuillaume Chatelet SweepMaxSize("sweep-max-size", 50deae7e98SGuillaume Chatelet cl::desc("The maximum size to use in sweep-mode"), 51deae7e98SGuillaume Chatelet cl::init(256)); 52deae7e98SGuillaume Chatelet 53deae7e98SGuillaume Chatelet static cl::opt<uint32_t> 54deae7e98SGuillaume Chatelet AlignedAccess("aligned-access", 55deae7e98SGuillaume Chatelet cl::desc("The alignment to use when accessing the buffers\n" 56deae7e98SGuillaume Chatelet "Default is unaligned\n" 57deae7e98SGuillaume Chatelet "Use 0 to disable address randomization"), 58deae7e98SGuillaume Chatelet cl::init(1)); 59deae7e98SGuillaume Chatelet 60deae7e98SGuillaume Chatelet static cl::opt<std::string> Output("output", 61deae7e98SGuillaume Chatelet cl::desc("Specify output filename"), 62438f7fc0SSiva Chandra Reddy cl::value_desc("filename"), cl::init("-")); 63438f7fc0SSiva Chandra Reddy 64deae7e98SGuillaume Chatelet static cl::opt<uint32_t> 65deae7e98SGuillaume Chatelet NumTrials("num-trials", cl::desc("The number of benchmarks run to perform"), 66deae7e98SGuillaume Chatelet cl::init(1)); 67438f7fc0SSiva Chandra Reddy 688d64ed85SGuillaume Chatelet #if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY) 69d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCPY 702f002817SAndre Vieira using BenchmarkSetup = CopySetup; 718d64ed85SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET) 72d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMSET 732f002817SAndre Vieira using BenchmarkSetup = SetSetup; 7487065c0dSGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_BZERO) 75d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BZERO 762f002817SAndre Vieira using BenchmarkSetup = SetSetup; 7787065c0dSGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP) 78d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP 792f002817SAndre Vieira using BenchmarkSetup = ComparisonSetup; 80*c8f79892SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_BCMP) 81*c8f79892SGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BCMP 82*c8f79892SGuillaume Chatelet using BenchmarkSetup = ComparisonSetup; 838d64ed85SGuillaume Chatelet #else 848d64ed85SGuillaume Chatelet #error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition" 858d64ed85SGuillaume Chatelet #endif 86deae7e98SGuillaume Chatelet 872f002817SAndre Vieira struct MemfunctionBenchmarkBase : public BenchmarkSetup { 88d3c70d9fSGuillaume Chatelet MemfunctionBenchmarkBase() : ReportProgress(isatty(fileno(stdout))) {} 89d3c70d9fSGuillaume Chatelet virtual ~MemfunctionBenchmarkBase() {} 90deae7e98SGuillaume Chatelet 91d3c70d9fSGuillaume Chatelet virtual Study run() = 0; 92d3c70d9fSGuillaume Chatelet 93d3c70d9fSGuillaume Chatelet CircularArrayRef<ParameterBatch::ParameterType> 94d3c70d9fSGuillaume Chatelet generateBatch(size_t Iterations) { 95d3c70d9fSGuillaume Chatelet randomize(); 96deae7e98SGuillaume Chatelet return cycle(makeArrayRef(Parameters), Iterations); 97deae7e98SGuillaume Chatelet } 98deae7e98SGuillaume Chatelet 99d3c70d9fSGuillaume Chatelet protected: 100d3c70d9fSGuillaume Chatelet Study createStudy() { 101d3c70d9fSGuillaume Chatelet Study Study; 1022f002817SAndre Vieira // Setup study. 103deae7e98SGuillaume Chatelet Study.StudyName = StudyName; 104deae7e98SGuillaume Chatelet Runtime &RI = Study.Runtime; 105deae7e98SGuillaume Chatelet RI.Host = HostState::get(); 106deae7e98SGuillaume Chatelet RI.BufferSize = BufferSize; 107d3c70d9fSGuillaume Chatelet RI.BatchParameterCount = BatchSize; 108deae7e98SGuillaume Chatelet 109deae7e98SGuillaume Chatelet BenchmarkOptions &BO = RI.BenchmarkOptions; 110deae7e98SGuillaume Chatelet BO.MinDuration = std::chrono::milliseconds(1); 111deae7e98SGuillaume Chatelet BO.MaxDuration = std::chrono::seconds(1); 112deae7e98SGuillaume Chatelet BO.MaxIterations = 10'000'000U; 113deae7e98SGuillaume Chatelet BO.MinSamples = 4; 114deae7e98SGuillaume Chatelet BO.MaxSamples = 1000; 115deae7e98SGuillaume Chatelet BO.Epsilon = 0.01; // 1% 116deae7e98SGuillaume Chatelet BO.ScalingFactor = 1.4; 117deae7e98SGuillaume Chatelet 118deae7e98SGuillaume Chatelet StudyConfiguration &SC = Study.Configuration; 119deae7e98SGuillaume Chatelet SC.NumTrials = NumTrials; 120deae7e98SGuillaume Chatelet SC.IsSweepMode = SweepMode; 121deae7e98SGuillaume Chatelet SC.AccessAlignment = MaybeAlign(AlignedAccess); 1228d64ed85SGuillaume Chatelet SC.Function = LIBC_BENCHMARK_FUNCTION_NAME; 123deae7e98SGuillaume Chatelet return Study; 124deae7e98SGuillaume Chatelet } 125deae7e98SGuillaume Chatelet 126d3c70d9fSGuillaume Chatelet void runTrials(const BenchmarkOptions &Options, 127d3c70d9fSGuillaume Chatelet std::vector<Duration> &Measurements) { 128d3c70d9fSGuillaume Chatelet for (size_t i = 0; i < NumTrials; ++i) { 129d3c70d9fSGuillaume Chatelet const BenchmarkResult Result = benchmark( 130d3c70d9fSGuillaume Chatelet Options, *this, [this](ParameterBatch::ParameterType Parameter) { 131d3c70d9fSGuillaume Chatelet return Call(Parameter, LIBC_BENCHMARK_FUNCTION); 132d3c70d9fSGuillaume Chatelet }); 133d3c70d9fSGuillaume Chatelet Measurements.push_back(Result.BestGuess); 134d3c70d9fSGuillaume Chatelet reportProgress(Measurements); 135d3c70d9fSGuillaume Chatelet } 136d3c70d9fSGuillaume Chatelet } 137d3c70d9fSGuillaume Chatelet 138d3c70d9fSGuillaume Chatelet virtual void randomize() = 0; 139d3c70d9fSGuillaume Chatelet 140deae7e98SGuillaume Chatelet private: 141d3c70d9fSGuillaume Chatelet bool ReportProgress; 142deae7e98SGuillaume Chatelet 143d3c70d9fSGuillaume Chatelet void reportProgress(const std::vector<Duration> &Measurements) { 144d3c70d9fSGuillaume Chatelet if (!ReportProgress) 145d3c70d9fSGuillaume Chatelet return; 146ab577807SGuillaume Chatelet static size_t LastPercent = -1; 147d3c70d9fSGuillaume Chatelet const size_t TotalSteps = Measurements.capacity(); 148d3c70d9fSGuillaume Chatelet const size_t Steps = Measurements.size(); 149deae7e98SGuillaume Chatelet const size_t Percent = 100 * Steps / TotalSteps; 150ab577807SGuillaume Chatelet if (Percent == LastPercent) 151ab577807SGuillaume Chatelet return; 152ab577807SGuillaume Chatelet LastPercent = Percent; 153deae7e98SGuillaume Chatelet size_t I = 0; 154deae7e98SGuillaume Chatelet errs() << '['; 155deae7e98SGuillaume Chatelet for (; I <= Percent; ++I) 156deae7e98SGuillaume Chatelet errs() << '#'; 157deae7e98SGuillaume Chatelet for (; I <= 100; ++I) 158deae7e98SGuillaume Chatelet errs() << '_'; 159ab577807SGuillaume Chatelet errs() << "] " << Percent << '%' << '\r'; 160deae7e98SGuillaume Chatelet } 161d3c70d9fSGuillaume Chatelet }; 162deae7e98SGuillaume Chatelet 163d3c70d9fSGuillaume Chatelet struct MemfunctionBenchmarkSweep final : public MemfunctionBenchmarkBase { 164d3c70d9fSGuillaume Chatelet MemfunctionBenchmarkSweep() 165d3c70d9fSGuillaume Chatelet : OffsetSampler(MemfunctionBenchmarkBase::BufferSize, SweepMaxSize, 166d3c70d9fSGuillaume Chatelet MaybeAlign(AlignedAccess)) {} 167d3c70d9fSGuillaume Chatelet 168d3c70d9fSGuillaume Chatelet virtual void randomize() override { 169d3c70d9fSGuillaume Chatelet for (auto &P : Parameters) { 170d3c70d9fSGuillaume Chatelet P.OffsetBytes = OffsetSampler(Gen); 171d3c70d9fSGuillaume Chatelet P.SizeBytes = CurrentSweepSize; 172d3c70d9fSGuillaume Chatelet checkValid(P); 173deae7e98SGuillaume Chatelet } 174deae7e98SGuillaume Chatelet } 175deae7e98SGuillaume Chatelet 176d3c70d9fSGuillaume Chatelet virtual Study run() override { 177d3c70d9fSGuillaume Chatelet Study Study = createStudy(); 178d3c70d9fSGuillaume Chatelet Study.Configuration.SweepModeMaxSize = SweepMaxSize; 179deae7e98SGuillaume Chatelet BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions; 180deae7e98SGuillaume Chatelet BO.MinDuration = std::chrono::milliseconds(1); 181deae7e98SGuillaume Chatelet BO.InitialIterations = 100; 182d3c70d9fSGuillaume Chatelet auto &Measurements = Study.Measurements; 183d3c70d9fSGuillaume Chatelet Measurements.reserve(NumTrials * SweepMaxSize); 184deae7e98SGuillaume Chatelet for (size_t Size = 0; Size <= SweepMaxSize; ++Size) { 185d3c70d9fSGuillaume Chatelet CurrentSweepSize = Size; 186d3c70d9fSGuillaume Chatelet runTrials(BO, Measurements); 187d3c70d9fSGuillaume Chatelet } 188d3c70d9fSGuillaume Chatelet return Study; 189d3c70d9fSGuillaume Chatelet } 190d3c70d9fSGuillaume Chatelet 191d3c70d9fSGuillaume Chatelet private: 192d3c70d9fSGuillaume Chatelet size_t CurrentSweepSize = 0; 193d3c70d9fSGuillaume Chatelet OffsetDistribution OffsetSampler; 194d3c70d9fSGuillaume Chatelet std::mt19937_64 Gen; 195d3c70d9fSGuillaume Chatelet }; 196d3c70d9fSGuillaume Chatelet 197d3c70d9fSGuillaume Chatelet struct MemfunctionBenchmarkDistribution final 198d3c70d9fSGuillaume Chatelet : public MemfunctionBenchmarkBase { 199d3c70d9fSGuillaume Chatelet MemfunctionBenchmarkDistribution(MemorySizeDistribution Distribution) 200d3c70d9fSGuillaume Chatelet : Distribution(Distribution), Probabilities(Distribution.Probabilities), 201d3c70d9fSGuillaume Chatelet SizeSampler(Probabilities.begin(), Probabilities.end()), 202d3c70d9fSGuillaume Chatelet OffsetSampler(MemfunctionBenchmarkBase::BufferSize, 203d3c70d9fSGuillaume Chatelet Probabilities.size() - 1, MaybeAlign(AlignedAccess)) {} 204d3c70d9fSGuillaume Chatelet 205d3c70d9fSGuillaume Chatelet virtual void randomize() override { 206d3c70d9fSGuillaume Chatelet for (auto &P : Parameters) { 207d3c70d9fSGuillaume Chatelet P.OffsetBytes = OffsetSampler(Gen); 208d3c70d9fSGuillaume Chatelet P.SizeBytes = SizeSampler(Gen); 209d3c70d9fSGuillaume Chatelet checkValid(P); 210deae7e98SGuillaume Chatelet } 211deae7e98SGuillaume Chatelet } 212deae7e98SGuillaume Chatelet 213d3c70d9fSGuillaume Chatelet virtual Study run() override { 214d3c70d9fSGuillaume Chatelet Study Study = createStudy(); 215d3c70d9fSGuillaume Chatelet Study.Configuration.SizeDistributionName = Distribution.Name.str(); 216deae7e98SGuillaume Chatelet BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions; 217deae7e98SGuillaume Chatelet BO.MinDuration = std::chrono::milliseconds(10); 218d3c70d9fSGuillaume Chatelet BO.InitialIterations = BatchSize * 10; 219d3c70d9fSGuillaume Chatelet auto &Measurements = Study.Measurements; 220d3c70d9fSGuillaume Chatelet Measurements.reserve(NumTrials); 221d3c70d9fSGuillaume Chatelet runTrials(BO, Measurements); 222d3c70d9fSGuillaume Chatelet return Study; 223deae7e98SGuillaume Chatelet } 224d3c70d9fSGuillaume Chatelet 225d3c70d9fSGuillaume Chatelet private: 226d3c70d9fSGuillaume Chatelet MemorySizeDistribution Distribution; 227d3c70d9fSGuillaume Chatelet ArrayRef<double> Probabilities; 228d3c70d9fSGuillaume Chatelet std::discrete_distribution<unsigned> SizeSampler; 229d3c70d9fSGuillaume Chatelet OffsetDistribution OffsetSampler; 230d3c70d9fSGuillaume Chatelet std::mt19937_64 Gen; 231deae7e98SGuillaume Chatelet }; 232deae7e98SGuillaume Chatelet 233deae7e98SGuillaume Chatelet void writeStudy(const Study &S) { 234438f7fc0SSiva Chandra Reddy std::error_code EC; 235438f7fc0SSiva Chandra Reddy raw_fd_ostream FOS(Output, EC); 236438f7fc0SSiva Chandra Reddy if (EC) 237438f7fc0SSiva Chandra Reddy report_fatal_error(Twine("Could not open file: ") 238438f7fc0SSiva Chandra Reddy .concat(EC.message()) 239438f7fc0SSiva Chandra Reddy .concat(", ") 240438f7fc0SSiva Chandra Reddy .concat(Output)); 241438f7fc0SSiva Chandra Reddy json::OStream JOS(FOS); 242deae7e98SGuillaume Chatelet serializeToJson(S, JOS); 243ab577807SGuillaume Chatelet FOS << "\n"; 244deae7e98SGuillaume Chatelet } 245deae7e98SGuillaume Chatelet 246deae7e98SGuillaume Chatelet void main() { 247deae7e98SGuillaume Chatelet checkRequirements(); 248d3c70d9fSGuillaume Chatelet if (!isPowerOf2_32(AlignedAccess)) 249d3c70d9fSGuillaume Chatelet report_fatal_error(AlignedAccess.ArgStr + 250d3c70d9fSGuillaume Chatelet Twine(" must be a power of two or zero")); 251d3c70d9fSGuillaume Chatelet 252d3c70d9fSGuillaume Chatelet const bool HasDistributionName = !SizeDistributionName.empty(); 253d3c70d9fSGuillaume Chatelet if (SweepMode && HasDistributionName) 254d3c70d9fSGuillaume Chatelet report_fatal_error("Select only one of `--" + Twine(SweepMode.ArgStr) + 255d3c70d9fSGuillaume Chatelet "` or `--" + Twine(SizeDistributionName.ArgStr) + "`"); 256d3c70d9fSGuillaume Chatelet 257d3c70d9fSGuillaume Chatelet std::unique_ptr<MemfunctionBenchmarkBase> Benchmark; 258d3c70d9fSGuillaume Chatelet if (SweepMode) 259d3c70d9fSGuillaume Chatelet Benchmark.reset(new MemfunctionBenchmarkSweep()); 260d3c70d9fSGuillaume Chatelet else 261d3c70d9fSGuillaume Chatelet Benchmark.reset(new MemfunctionBenchmarkDistribution(getDistributionOrDie( 2622f002817SAndre Vieira BenchmarkSetup::getDistributions(), SizeDistributionName))); 263d3c70d9fSGuillaume Chatelet writeStudy(Benchmark->run()); 264438f7fc0SSiva Chandra Reddy } 265438f7fc0SSiva Chandra Reddy 266438f7fc0SSiva Chandra Reddy } // namespace libc_benchmarks 267438f7fc0SSiva Chandra Reddy } // namespace llvm 268438f7fc0SSiva Chandra Reddy 269d3c70d9fSGuillaume Chatelet #ifndef NDEBUG 270d3c70d9fSGuillaume Chatelet #error For reproducibility benchmarks should not be compiled in DEBUG mode. 271d3c70d9fSGuillaume Chatelet #endif 272d3c70d9fSGuillaume Chatelet 273438f7fc0SSiva Chandra Reddy int main(int argc, char **argv) { 274438f7fc0SSiva Chandra Reddy llvm::cl::ParseCommandLineOptions(argc, argv); 275deae7e98SGuillaume Chatelet llvm::libc_benchmarks::main(); 276438f7fc0SSiva Chandra Reddy return EXIT_SUCCESS; 277438f7fc0SSiva Chandra Reddy } 278