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