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" 13*5ff3ff33SPetr Hosek #include "src/__support/macros/config.h" 14438f7fc0SSiva Chandra Reddy #include "llvm/Support/CommandLine.h" 15438f7fc0SSiva Chandra Reddy #include "llvm/Support/ErrorHandling.h" 16438f7fc0SSiva Chandra Reddy #include "llvm/Support/FileSystem.h" 17438f7fc0SSiva Chandra Reddy #include "llvm/Support/JSON.h" 18d3c70d9fSGuillaume Chatelet #include "llvm/Support/MathExtras.h" 19438f7fc0SSiva Chandra Reddy #include "llvm/Support/MemoryBuffer.h" 20438f7fc0SSiva Chandra Reddy #include "llvm/Support/raw_ostream.h" 21438f7fc0SSiva Chandra Reddy 2287065c0dSGuillaume Chatelet #include <cstring> 23d3c70d9fSGuillaume Chatelet #include <unistd.h> 2487065c0dSGuillaume Chatelet 25*5ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL { 26deae7e98SGuillaume Chatelet 27deae7e98SGuillaume Chatelet extern void *memcpy(void *__restrict, const void *__restrict, size_t); 28de21f346SGuillaume Chatelet extern void *memmove(void *, const void *, size_t); 29deae7e98SGuillaume Chatelet extern void *memset(void *, int, size_t); 3087065c0dSGuillaume Chatelet extern void bzero(void *, size_t); 3187065c0dSGuillaume Chatelet extern int memcmp(const void *, const void *, size_t); 32c8f79892SGuillaume Chatelet extern int bcmp(const void *, const void *, size_t); 33deae7e98SGuillaume Chatelet 34*5ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL 35438f7fc0SSiva Chandra Reddy 36438f7fc0SSiva Chandra Reddy namespace llvm { 37438f7fc0SSiva Chandra Reddy namespace libc_benchmarks { 38438f7fc0SSiva Chandra Reddy 39deae7e98SGuillaume Chatelet static cl::opt<std::string> 40deae7e98SGuillaume Chatelet StudyName("study-name", cl::desc("The name for this study"), cl::Required); 41deae7e98SGuillaume Chatelet 42deae7e98SGuillaume Chatelet static cl::opt<std::string> 43deae7e98SGuillaume Chatelet SizeDistributionName("size-distribution-name", 44deae7e98SGuillaume Chatelet cl::desc("The name of the distribution to use")); 45deae7e98SGuillaume Chatelet 46605fadf0SDmitry Vyukov static cl::opt<bool> SweepMode( 47605fadf0SDmitry Vyukov "sweep-mode", 48605fadf0SDmitry Vyukov cl::desc( 49605fadf0SDmitry Vyukov "If set, benchmark all sizes from sweep-min-size to sweep-max-size")); 50605fadf0SDmitry Vyukov 51605fadf0SDmitry Vyukov static cl::opt<uint32_t> 52605fadf0SDmitry Vyukov SweepMinSize("sweep-min-size", 53605fadf0SDmitry Vyukov cl::desc("The minimum size to use in sweep-mode"), 54605fadf0SDmitry Vyukov cl::init(0)); 55deae7e98SGuillaume Chatelet 56deae7e98SGuillaume Chatelet static cl::opt<uint32_t> 57deae7e98SGuillaume Chatelet SweepMaxSize("sweep-max-size", 58deae7e98SGuillaume Chatelet cl::desc("The maximum size to use in sweep-mode"), 59deae7e98SGuillaume Chatelet cl::init(256)); 60deae7e98SGuillaume Chatelet 61deae7e98SGuillaume Chatelet static cl::opt<uint32_t> 62deae7e98SGuillaume Chatelet AlignedAccess("aligned-access", 63deae7e98SGuillaume Chatelet cl::desc("The alignment to use when accessing the buffers\n" 64deae7e98SGuillaume Chatelet "Default is unaligned\n" 65deae7e98SGuillaume Chatelet "Use 0 to disable address randomization"), 66deae7e98SGuillaume Chatelet cl::init(1)); 67deae7e98SGuillaume Chatelet 68deae7e98SGuillaume Chatelet static cl::opt<std::string> Output("output", 69deae7e98SGuillaume Chatelet cl::desc("Specify output filename"), 70438f7fc0SSiva Chandra Reddy cl::value_desc("filename"), cl::init("-")); 71438f7fc0SSiva Chandra Reddy 72deae7e98SGuillaume Chatelet static cl::opt<uint32_t> 73deae7e98SGuillaume Chatelet NumTrials("num-trials", cl::desc("The number of benchmarks run to perform"), 74deae7e98SGuillaume Chatelet cl::init(1)); 75438f7fc0SSiva Chandra Reddy 768d64ed85SGuillaume Chatelet #if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY) 77d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCPY 782f002817SAndre Vieira using BenchmarkSetup = CopySetup; 79de21f346SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMMOVE) 80de21f346SGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMMOVE 81de21f346SGuillaume Chatelet using BenchmarkSetup = MoveSetup; 828d64ed85SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET) 83d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMSET 842f002817SAndre Vieira using BenchmarkSetup = SetSetup; 8587065c0dSGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_BZERO) 86d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BZERO 872f002817SAndre Vieira using BenchmarkSetup = SetSetup; 8887065c0dSGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP) 89d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP 902f002817SAndre Vieira using BenchmarkSetup = ComparisonSetup; 91c8f79892SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_BCMP) 92c8f79892SGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BCMP 93c8f79892SGuillaume Chatelet using BenchmarkSetup = ComparisonSetup; 948d64ed85SGuillaume Chatelet #else 958d64ed85SGuillaume Chatelet #error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition" 968d64ed85SGuillaume Chatelet #endif 97deae7e98SGuillaume Chatelet 982f002817SAndre Vieira struct MemfunctionBenchmarkBase : public BenchmarkSetup { 99d3c70d9fSGuillaume Chatelet MemfunctionBenchmarkBase() : ReportProgress(isatty(fileno(stdout))) {} 100d3c70d9fSGuillaume Chatelet virtual ~MemfunctionBenchmarkBase() {} 101deae7e98SGuillaume Chatelet 102d3c70d9fSGuillaume Chatelet virtual Study run() = 0; 103d3c70d9fSGuillaume Chatelet 104d3c70d9fSGuillaume Chatelet CircularArrayRef<ParameterBatch::ParameterType> 105d3c70d9fSGuillaume Chatelet generateBatch(size_t Iterations) { 106d3c70d9fSGuillaume Chatelet randomize(); 107984b800aSserge-sans-paille return cycle(ArrayRef(Parameters), Iterations); 108deae7e98SGuillaume Chatelet } 109deae7e98SGuillaume Chatelet 110d3c70d9fSGuillaume Chatelet protected: 111d3c70d9fSGuillaume Chatelet Study createStudy() { 112d3c70d9fSGuillaume Chatelet Study Study; 1132f002817SAndre Vieira // Setup study. 114deae7e98SGuillaume Chatelet Study.StudyName = StudyName; 115deae7e98SGuillaume Chatelet Runtime &RI = Study.Runtime; 116deae7e98SGuillaume Chatelet RI.Host = HostState::get(); 117deae7e98SGuillaume Chatelet RI.BufferSize = BufferSize; 118d3c70d9fSGuillaume Chatelet RI.BatchParameterCount = BatchSize; 119deae7e98SGuillaume Chatelet 120deae7e98SGuillaume Chatelet BenchmarkOptions &BO = RI.BenchmarkOptions; 121deae7e98SGuillaume Chatelet BO.MinDuration = std::chrono::milliseconds(1); 122deae7e98SGuillaume Chatelet BO.MaxDuration = std::chrono::seconds(1); 123deae7e98SGuillaume Chatelet BO.MaxIterations = 10'000'000U; 124deae7e98SGuillaume Chatelet BO.MinSamples = 4; 125deae7e98SGuillaume Chatelet BO.MaxSamples = 1000; 126deae7e98SGuillaume Chatelet BO.Epsilon = 0.01; // 1% 127deae7e98SGuillaume Chatelet BO.ScalingFactor = 1.4; 128deae7e98SGuillaume Chatelet 129deae7e98SGuillaume Chatelet StudyConfiguration &SC = Study.Configuration; 130deae7e98SGuillaume Chatelet SC.NumTrials = NumTrials; 131deae7e98SGuillaume Chatelet SC.IsSweepMode = SweepMode; 132deae7e98SGuillaume Chatelet SC.AccessAlignment = MaybeAlign(AlignedAccess); 1338d64ed85SGuillaume Chatelet SC.Function = LIBC_BENCHMARK_FUNCTION_NAME; 134deae7e98SGuillaume Chatelet return Study; 135deae7e98SGuillaume Chatelet } 136deae7e98SGuillaume Chatelet 137d3c70d9fSGuillaume Chatelet void runTrials(const BenchmarkOptions &Options, 138d3c70d9fSGuillaume Chatelet std::vector<Duration> &Measurements) { 139d3c70d9fSGuillaume Chatelet for (size_t i = 0; i < NumTrials; ++i) { 140d3c70d9fSGuillaume Chatelet const BenchmarkResult Result = benchmark( 141d3c70d9fSGuillaume Chatelet Options, *this, [this](ParameterBatch::ParameterType Parameter) { 142d3c70d9fSGuillaume Chatelet return Call(Parameter, LIBC_BENCHMARK_FUNCTION); 143d3c70d9fSGuillaume Chatelet }); 144d3c70d9fSGuillaume Chatelet Measurements.push_back(Result.BestGuess); 145d3c70d9fSGuillaume Chatelet reportProgress(Measurements); 146d3c70d9fSGuillaume Chatelet } 147d3c70d9fSGuillaume Chatelet } 148d3c70d9fSGuillaume Chatelet 149d3c70d9fSGuillaume Chatelet virtual void randomize() = 0; 150d3c70d9fSGuillaume Chatelet 151deae7e98SGuillaume Chatelet private: 152d3c70d9fSGuillaume Chatelet bool ReportProgress; 153deae7e98SGuillaume Chatelet 154d3c70d9fSGuillaume Chatelet void reportProgress(const std::vector<Duration> &Measurements) { 155d3c70d9fSGuillaume Chatelet if (!ReportProgress) 156d3c70d9fSGuillaume Chatelet return; 157ab577807SGuillaume Chatelet static size_t LastPercent = -1; 158d3c70d9fSGuillaume Chatelet const size_t TotalSteps = Measurements.capacity(); 159d3c70d9fSGuillaume Chatelet const size_t Steps = Measurements.size(); 160deae7e98SGuillaume Chatelet const size_t Percent = 100 * Steps / TotalSteps; 161ab577807SGuillaume Chatelet if (Percent == LastPercent) 162ab577807SGuillaume Chatelet return; 163ab577807SGuillaume Chatelet LastPercent = Percent; 164deae7e98SGuillaume Chatelet size_t I = 0; 165deae7e98SGuillaume Chatelet errs() << '['; 166deae7e98SGuillaume Chatelet for (; I <= Percent; ++I) 167deae7e98SGuillaume Chatelet errs() << '#'; 168deae7e98SGuillaume Chatelet for (; I <= 100; ++I) 169deae7e98SGuillaume Chatelet errs() << '_'; 170ab577807SGuillaume Chatelet errs() << "] " << Percent << '%' << '\r'; 171deae7e98SGuillaume Chatelet } 172d3c70d9fSGuillaume Chatelet }; 173deae7e98SGuillaume Chatelet 174d3c70d9fSGuillaume Chatelet struct MemfunctionBenchmarkSweep final : public MemfunctionBenchmarkBase { 175d3c70d9fSGuillaume Chatelet MemfunctionBenchmarkSweep() 176d3c70d9fSGuillaume Chatelet : OffsetSampler(MemfunctionBenchmarkBase::BufferSize, SweepMaxSize, 177d3c70d9fSGuillaume Chatelet MaybeAlign(AlignedAccess)) {} 178d3c70d9fSGuillaume Chatelet 179d3c70d9fSGuillaume Chatelet virtual void randomize() override { 180d3c70d9fSGuillaume Chatelet for (auto &P : Parameters) { 181d3c70d9fSGuillaume Chatelet P.OffsetBytes = OffsetSampler(Gen); 182d3c70d9fSGuillaume Chatelet P.SizeBytes = CurrentSweepSize; 183d3c70d9fSGuillaume Chatelet checkValid(P); 184deae7e98SGuillaume Chatelet } 185deae7e98SGuillaume Chatelet } 186deae7e98SGuillaume Chatelet 187d3c70d9fSGuillaume Chatelet virtual Study run() override { 188d3c70d9fSGuillaume Chatelet Study Study = createStudy(); 189d3c70d9fSGuillaume Chatelet Study.Configuration.SweepModeMaxSize = SweepMaxSize; 190deae7e98SGuillaume Chatelet BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions; 191deae7e98SGuillaume Chatelet BO.MinDuration = std::chrono::milliseconds(1); 192deae7e98SGuillaume Chatelet BO.InitialIterations = 100; 193d3c70d9fSGuillaume Chatelet auto &Measurements = Study.Measurements; 194d3c70d9fSGuillaume Chatelet Measurements.reserve(NumTrials * SweepMaxSize); 195605fadf0SDmitry Vyukov for (size_t Size = SweepMinSize; Size <= SweepMaxSize; ++Size) { 196d3c70d9fSGuillaume Chatelet CurrentSweepSize = Size; 197d3c70d9fSGuillaume Chatelet runTrials(BO, Measurements); 198d3c70d9fSGuillaume Chatelet } 199d3c70d9fSGuillaume Chatelet return Study; 200d3c70d9fSGuillaume Chatelet } 201d3c70d9fSGuillaume Chatelet 202d3c70d9fSGuillaume Chatelet private: 203d3c70d9fSGuillaume Chatelet size_t CurrentSweepSize = 0; 204d3c70d9fSGuillaume Chatelet OffsetDistribution OffsetSampler; 205d3c70d9fSGuillaume Chatelet std::mt19937_64 Gen; 206d3c70d9fSGuillaume Chatelet }; 207d3c70d9fSGuillaume Chatelet 208d3c70d9fSGuillaume Chatelet struct MemfunctionBenchmarkDistribution final 209d3c70d9fSGuillaume Chatelet : public MemfunctionBenchmarkBase { 210d3c70d9fSGuillaume Chatelet MemfunctionBenchmarkDistribution(MemorySizeDistribution Distribution) 211d3c70d9fSGuillaume Chatelet : Distribution(Distribution), Probabilities(Distribution.Probabilities), 212d3c70d9fSGuillaume Chatelet SizeSampler(Probabilities.begin(), Probabilities.end()), 213d3c70d9fSGuillaume Chatelet OffsetSampler(MemfunctionBenchmarkBase::BufferSize, 214d3c70d9fSGuillaume Chatelet Probabilities.size() - 1, MaybeAlign(AlignedAccess)) {} 215d3c70d9fSGuillaume Chatelet 216d3c70d9fSGuillaume Chatelet virtual void randomize() override { 217d3c70d9fSGuillaume Chatelet for (auto &P : Parameters) { 218d3c70d9fSGuillaume Chatelet P.OffsetBytes = OffsetSampler(Gen); 219d3c70d9fSGuillaume Chatelet P.SizeBytes = SizeSampler(Gen); 220d3c70d9fSGuillaume Chatelet checkValid(P); 221deae7e98SGuillaume Chatelet } 222deae7e98SGuillaume Chatelet } 223deae7e98SGuillaume Chatelet 224d3c70d9fSGuillaume Chatelet virtual Study run() override { 225d3c70d9fSGuillaume Chatelet Study Study = createStudy(); 226d3c70d9fSGuillaume Chatelet Study.Configuration.SizeDistributionName = Distribution.Name.str(); 227deae7e98SGuillaume Chatelet BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions; 228deae7e98SGuillaume Chatelet BO.MinDuration = std::chrono::milliseconds(10); 229d3c70d9fSGuillaume Chatelet BO.InitialIterations = BatchSize * 10; 230d3c70d9fSGuillaume Chatelet auto &Measurements = Study.Measurements; 231d3c70d9fSGuillaume Chatelet Measurements.reserve(NumTrials); 232d3c70d9fSGuillaume Chatelet runTrials(BO, Measurements); 233d3c70d9fSGuillaume Chatelet return Study; 234deae7e98SGuillaume Chatelet } 235d3c70d9fSGuillaume Chatelet 236d3c70d9fSGuillaume Chatelet private: 237d3c70d9fSGuillaume Chatelet MemorySizeDistribution Distribution; 238d3c70d9fSGuillaume Chatelet ArrayRef<double> Probabilities; 239d3c70d9fSGuillaume Chatelet std::discrete_distribution<unsigned> SizeSampler; 240d3c70d9fSGuillaume Chatelet OffsetDistribution OffsetSampler; 241d3c70d9fSGuillaume Chatelet std::mt19937_64 Gen; 242deae7e98SGuillaume Chatelet }; 243deae7e98SGuillaume Chatelet 244deae7e98SGuillaume Chatelet void writeStudy(const Study &S) { 245438f7fc0SSiva Chandra Reddy std::error_code EC; 246438f7fc0SSiva Chandra Reddy raw_fd_ostream FOS(Output, EC); 247438f7fc0SSiva Chandra Reddy if (EC) 248438f7fc0SSiva Chandra Reddy report_fatal_error(Twine("Could not open file: ") 249438f7fc0SSiva Chandra Reddy .concat(EC.message()) 250438f7fc0SSiva Chandra Reddy .concat(", ") 251438f7fc0SSiva Chandra Reddy .concat(Output)); 252438f7fc0SSiva Chandra Reddy json::OStream JOS(FOS); 253deae7e98SGuillaume Chatelet serializeToJson(S, JOS); 254ab577807SGuillaume Chatelet FOS << "\n"; 255deae7e98SGuillaume Chatelet } 256deae7e98SGuillaume Chatelet 257deae7e98SGuillaume Chatelet void main() { 258deae7e98SGuillaume Chatelet checkRequirements(); 259d3c70d9fSGuillaume Chatelet if (!isPowerOf2_32(AlignedAccess)) 260d3c70d9fSGuillaume Chatelet report_fatal_error(AlignedAccess.ArgStr + 261d3c70d9fSGuillaume Chatelet Twine(" must be a power of two or zero")); 262d3c70d9fSGuillaume Chatelet 263d3c70d9fSGuillaume Chatelet const bool HasDistributionName = !SizeDistributionName.empty(); 264d3c70d9fSGuillaume Chatelet if (SweepMode && HasDistributionName) 265d3c70d9fSGuillaume Chatelet report_fatal_error("Select only one of `--" + Twine(SweepMode.ArgStr) + 266d3c70d9fSGuillaume Chatelet "` or `--" + Twine(SizeDistributionName.ArgStr) + "`"); 267d3c70d9fSGuillaume Chatelet 268d3c70d9fSGuillaume Chatelet std::unique_ptr<MemfunctionBenchmarkBase> Benchmark; 269d3c70d9fSGuillaume Chatelet if (SweepMode) 270d3c70d9fSGuillaume Chatelet Benchmark.reset(new MemfunctionBenchmarkSweep()); 271d3c70d9fSGuillaume Chatelet else 272d3c70d9fSGuillaume Chatelet Benchmark.reset(new MemfunctionBenchmarkDistribution(getDistributionOrDie( 2732f002817SAndre Vieira BenchmarkSetup::getDistributions(), SizeDistributionName))); 274d3c70d9fSGuillaume Chatelet writeStudy(Benchmark->run()); 275438f7fc0SSiva Chandra Reddy } 276438f7fc0SSiva Chandra Reddy 277438f7fc0SSiva Chandra Reddy } // namespace libc_benchmarks 278438f7fc0SSiva Chandra Reddy } // namespace llvm 279438f7fc0SSiva Chandra Reddy 280d3c70d9fSGuillaume Chatelet #ifndef NDEBUG 281d3c70d9fSGuillaume Chatelet #error For reproducibility benchmarks should not be compiled in DEBUG mode. 282d3c70d9fSGuillaume Chatelet #endif 283d3c70d9fSGuillaume Chatelet 284438f7fc0SSiva Chandra Reddy int main(int argc, char **argv) { 285438f7fc0SSiva Chandra Reddy llvm::cl::ParseCommandLineOptions(argc, argv); 286deae7e98SGuillaume Chatelet llvm::libc_benchmarks::main(); 287438f7fc0SSiva Chandra Reddy return EXIT_SUCCESS; 288438f7fc0SSiva Chandra Reddy } 289