xref: /llvm-project/libc/benchmarks/LibcMemoryBenchmarkMain.cpp (revision 5ff3ff33ff930e4ec49da7910612d8a41eb068cb)
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