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);
27*de21f346SGuillaume Chatelet extern void *memmove(void *, const void *, size_t);
28deae7e98SGuillaume Chatelet extern void *memset(void *, int, size_t);
2987065c0dSGuillaume Chatelet extern void bzero(void *, size_t);
3087065c0dSGuillaume Chatelet extern int memcmp(const void *, const void *, size_t);
31c8f79892SGuillaume Chatelet extern int bcmp(const void *, const void *, size_t);
32deae7e98SGuillaume Chatelet 
33deae7e98SGuillaume Chatelet } // namespace __llvm_libc
34438f7fc0SSiva Chandra Reddy 
35438f7fc0SSiva Chandra Reddy namespace llvm {
36438f7fc0SSiva Chandra Reddy namespace libc_benchmarks {
37438f7fc0SSiva Chandra Reddy 
38deae7e98SGuillaume Chatelet static cl::opt<std::string>
39deae7e98SGuillaume Chatelet     StudyName("study-name", cl::desc("The name for this study"), cl::Required);
40deae7e98SGuillaume Chatelet 
41deae7e98SGuillaume Chatelet static cl::opt<std::string>
42deae7e98SGuillaume Chatelet     SizeDistributionName("size-distribution-name",
43deae7e98SGuillaume Chatelet                          cl::desc("The name of the distribution to use"));
44deae7e98SGuillaume Chatelet 
45deae7e98SGuillaume Chatelet static cl::opt<bool>
46deae7e98SGuillaume Chatelet     SweepMode("sweep-mode",
47deae7e98SGuillaume Chatelet               cl::desc("If set, benchmark all sizes from 0 to sweep-max-size"));
48deae7e98SGuillaume Chatelet 
49deae7e98SGuillaume Chatelet static cl::opt<uint32_t>
50deae7e98SGuillaume Chatelet     SweepMaxSize("sweep-max-size",
51deae7e98SGuillaume Chatelet                  cl::desc("The maximum size to use in sweep-mode"),
52deae7e98SGuillaume Chatelet                  cl::init(256));
53deae7e98SGuillaume Chatelet 
54deae7e98SGuillaume Chatelet static cl::opt<uint32_t>
55deae7e98SGuillaume Chatelet     AlignedAccess("aligned-access",
56deae7e98SGuillaume Chatelet                   cl::desc("The alignment to use when accessing the buffers\n"
57deae7e98SGuillaume Chatelet                            "Default is unaligned\n"
58deae7e98SGuillaume Chatelet                            "Use 0 to disable address randomization"),
59deae7e98SGuillaume Chatelet                   cl::init(1));
60deae7e98SGuillaume Chatelet 
61deae7e98SGuillaume Chatelet static cl::opt<std::string> Output("output",
62deae7e98SGuillaume Chatelet                                    cl::desc("Specify output filename"),
63438f7fc0SSiva Chandra Reddy                                    cl::value_desc("filename"), cl::init("-"));
64438f7fc0SSiva Chandra Reddy 
65deae7e98SGuillaume Chatelet static cl::opt<uint32_t>
66deae7e98SGuillaume Chatelet     NumTrials("num-trials", cl::desc("The number of benchmarks run to perform"),
67deae7e98SGuillaume Chatelet               cl::init(1));
68438f7fc0SSiva Chandra Reddy 
698d64ed85SGuillaume Chatelet #if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY)
70d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCPY
712f002817SAndre Vieira using BenchmarkSetup = CopySetup;
72*de21f346SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMMOVE)
73*de21f346SGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMMOVE
74*de21f346SGuillaume Chatelet using BenchmarkSetup = MoveSetup;
758d64ed85SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET)
76d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMSET
772f002817SAndre Vieira using BenchmarkSetup = SetSetup;
7887065c0dSGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_BZERO)
79d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BZERO
802f002817SAndre Vieira using BenchmarkSetup = SetSetup;
8187065c0dSGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP)
82d3c70d9fSGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP
832f002817SAndre Vieira using BenchmarkSetup = ComparisonSetup;
84c8f79892SGuillaume Chatelet #elif defined(LIBC_BENCHMARK_FUNCTION_BCMP)
85c8f79892SGuillaume Chatelet #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BCMP
86c8f79892SGuillaume Chatelet using BenchmarkSetup = ComparisonSetup;
878d64ed85SGuillaume Chatelet #else
888d64ed85SGuillaume Chatelet #error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition"
898d64ed85SGuillaume Chatelet #endif
90deae7e98SGuillaume Chatelet 
912f002817SAndre Vieira struct MemfunctionBenchmarkBase : public BenchmarkSetup {
MemfunctionBenchmarkBasellvm::libc_benchmarks::MemfunctionBenchmarkBase92d3c70d9fSGuillaume Chatelet   MemfunctionBenchmarkBase() : ReportProgress(isatty(fileno(stdout))) {}
~MemfunctionBenchmarkBasellvm::libc_benchmarks::MemfunctionBenchmarkBase93d3c70d9fSGuillaume Chatelet   virtual ~MemfunctionBenchmarkBase() {}
94deae7e98SGuillaume Chatelet 
95d3c70d9fSGuillaume Chatelet   virtual Study run() = 0;
96d3c70d9fSGuillaume Chatelet 
97d3c70d9fSGuillaume Chatelet   CircularArrayRef<ParameterBatch::ParameterType>
generateBatchllvm::libc_benchmarks::MemfunctionBenchmarkBase98d3c70d9fSGuillaume Chatelet   generateBatch(size_t Iterations) {
99d3c70d9fSGuillaume Chatelet     randomize();
100deae7e98SGuillaume Chatelet     return cycle(makeArrayRef(Parameters), Iterations);
101deae7e98SGuillaume Chatelet   }
102deae7e98SGuillaume Chatelet 
103d3c70d9fSGuillaume Chatelet protected:
createStudyllvm::libc_benchmarks::MemfunctionBenchmarkBase104d3c70d9fSGuillaume Chatelet   Study createStudy() {
105d3c70d9fSGuillaume Chatelet     Study Study;
1062f002817SAndre Vieira     // Setup study.
107deae7e98SGuillaume Chatelet     Study.StudyName = StudyName;
108deae7e98SGuillaume Chatelet     Runtime &RI = Study.Runtime;
109deae7e98SGuillaume Chatelet     RI.Host = HostState::get();
110deae7e98SGuillaume Chatelet     RI.BufferSize = BufferSize;
111d3c70d9fSGuillaume Chatelet     RI.BatchParameterCount = BatchSize;
112deae7e98SGuillaume Chatelet 
113deae7e98SGuillaume Chatelet     BenchmarkOptions &BO = RI.BenchmarkOptions;
114deae7e98SGuillaume Chatelet     BO.MinDuration = std::chrono::milliseconds(1);
115deae7e98SGuillaume Chatelet     BO.MaxDuration = std::chrono::seconds(1);
116deae7e98SGuillaume Chatelet     BO.MaxIterations = 10'000'000U;
117deae7e98SGuillaume Chatelet     BO.MinSamples = 4;
118deae7e98SGuillaume Chatelet     BO.MaxSamples = 1000;
119deae7e98SGuillaume Chatelet     BO.Epsilon = 0.01; // 1%
120deae7e98SGuillaume Chatelet     BO.ScalingFactor = 1.4;
121deae7e98SGuillaume Chatelet 
122deae7e98SGuillaume Chatelet     StudyConfiguration &SC = Study.Configuration;
123deae7e98SGuillaume Chatelet     SC.NumTrials = NumTrials;
124deae7e98SGuillaume Chatelet     SC.IsSweepMode = SweepMode;
125deae7e98SGuillaume Chatelet     SC.AccessAlignment = MaybeAlign(AlignedAccess);
1268d64ed85SGuillaume Chatelet     SC.Function = LIBC_BENCHMARK_FUNCTION_NAME;
127deae7e98SGuillaume Chatelet     return Study;
128deae7e98SGuillaume Chatelet   }
129deae7e98SGuillaume Chatelet 
runTrialsllvm::libc_benchmarks::MemfunctionBenchmarkBase130d3c70d9fSGuillaume Chatelet   void runTrials(const BenchmarkOptions &Options,
131d3c70d9fSGuillaume Chatelet                  std::vector<Duration> &Measurements) {
132d3c70d9fSGuillaume Chatelet     for (size_t i = 0; i < NumTrials; ++i) {
133d3c70d9fSGuillaume Chatelet       const BenchmarkResult Result = benchmark(
134d3c70d9fSGuillaume Chatelet           Options, *this, [this](ParameterBatch::ParameterType Parameter) {
135d3c70d9fSGuillaume Chatelet             return Call(Parameter, LIBC_BENCHMARK_FUNCTION);
136d3c70d9fSGuillaume Chatelet           });
137d3c70d9fSGuillaume Chatelet       Measurements.push_back(Result.BestGuess);
138d3c70d9fSGuillaume Chatelet       reportProgress(Measurements);
139d3c70d9fSGuillaume Chatelet     }
140d3c70d9fSGuillaume Chatelet   }
141d3c70d9fSGuillaume Chatelet 
142d3c70d9fSGuillaume Chatelet   virtual void randomize() = 0;
143d3c70d9fSGuillaume Chatelet 
144deae7e98SGuillaume Chatelet private:
145d3c70d9fSGuillaume Chatelet   bool ReportProgress;
146deae7e98SGuillaume Chatelet 
reportProgressllvm::libc_benchmarks::MemfunctionBenchmarkBase147d3c70d9fSGuillaume Chatelet   void reportProgress(const std::vector<Duration> &Measurements) {
148d3c70d9fSGuillaume Chatelet     if (!ReportProgress)
149d3c70d9fSGuillaume Chatelet       return;
150ab577807SGuillaume Chatelet     static size_t LastPercent = -1;
151d3c70d9fSGuillaume Chatelet     const size_t TotalSteps = Measurements.capacity();
152d3c70d9fSGuillaume Chatelet     const size_t Steps = Measurements.size();
153deae7e98SGuillaume Chatelet     const size_t Percent = 100 * Steps / TotalSteps;
154ab577807SGuillaume Chatelet     if (Percent == LastPercent)
155ab577807SGuillaume Chatelet       return;
156ab577807SGuillaume Chatelet     LastPercent = Percent;
157deae7e98SGuillaume Chatelet     size_t I = 0;
158deae7e98SGuillaume Chatelet     errs() << '[';
159deae7e98SGuillaume Chatelet     for (; I <= Percent; ++I)
160deae7e98SGuillaume Chatelet       errs() << '#';
161deae7e98SGuillaume Chatelet     for (; I <= 100; ++I)
162deae7e98SGuillaume Chatelet       errs() << '_';
163ab577807SGuillaume Chatelet     errs() << "] " << Percent << '%' << '\r';
164deae7e98SGuillaume Chatelet   }
165d3c70d9fSGuillaume Chatelet };
166deae7e98SGuillaume Chatelet 
167d3c70d9fSGuillaume Chatelet struct MemfunctionBenchmarkSweep final : public MemfunctionBenchmarkBase {
MemfunctionBenchmarkSweepllvm::libc_benchmarks::MemfunctionBenchmarkSweep168d3c70d9fSGuillaume Chatelet   MemfunctionBenchmarkSweep()
169d3c70d9fSGuillaume Chatelet       : OffsetSampler(MemfunctionBenchmarkBase::BufferSize, SweepMaxSize,
170d3c70d9fSGuillaume Chatelet                       MaybeAlign(AlignedAccess)) {}
171d3c70d9fSGuillaume Chatelet 
randomizellvm::libc_benchmarks::MemfunctionBenchmarkSweep172d3c70d9fSGuillaume Chatelet   virtual void randomize() override {
173d3c70d9fSGuillaume Chatelet     for (auto &P : Parameters) {
174d3c70d9fSGuillaume Chatelet       P.OffsetBytes = OffsetSampler(Gen);
175d3c70d9fSGuillaume Chatelet       P.SizeBytes = CurrentSweepSize;
176d3c70d9fSGuillaume Chatelet       checkValid(P);
177deae7e98SGuillaume Chatelet     }
178deae7e98SGuillaume Chatelet   }
179deae7e98SGuillaume Chatelet 
runllvm::libc_benchmarks::MemfunctionBenchmarkSweep180d3c70d9fSGuillaume Chatelet   virtual Study run() override {
181d3c70d9fSGuillaume Chatelet     Study Study = createStudy();
182d3c70d9fSGuillaume Chatelet     Study.Configuration.SweepModeMaxSize = SweepMaxSize;
183deae7e98SGuillaume Chatelet     BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions;
184deae7e98SGuillaume Chatelet     BO.MinDuration = std::chrono::milliseconds(1);
185deae7e98SGuillaume Chatelet     BO.InitialIterations = 100;
186d3c70d9fSGuillaume Chatelet     auto &Measurements = Study.Measurements;
187d3c70d9fSGuillaume Chatelet     Measurements.reserve(NumTrials * SweepMaxSize);
188deae7e98SGuillaume Chatelet     for (size_t Size = 0; Size <= SweepMaxSize; ++Size) {
189d3c70d9fSGuillaume Chatelet       CurrentSweepSize = Size;
190d3c70d9fSGuillaume Chatelet       runTrials(BO, Measurements);
191d3c70d9fSGuillaume Chatelet     }
192d3c70d9fSGuillaume Chatelet     return Study;
193d3c70d9fSGuillaume Chatelet   }
194d3c70d9fSGuillaume Chatelet 
195d3c70d9fSGuillaume Chatelet private:
196d3c70d9fSGuillaume Chatelet   size_t CurrentSweepSize = 0;
197d3c70d9fSGuillaume Chatelet   OffsetDistribution OffsetSampler;
198d3c70d9fSGuillaume Chatelet   std::mt19937_64 Gen;
199d3c70d9fSGuillaume Chatelet };
200d3c70d9fSGuillaume Chatelet 
201d3c70d9fSGuillaume Chatelet struct MemfunctionBenchmarkDistribution final
202d3c70d9fSGuillaume Chatelet     : public MemfunctionBenchmarkBase {
MemfunctionBenchmarkDistributionllvm::libc_benchmarks::MemfunctionBenchmarkDistribution203d3c70d9fSGuillaume Chatelet   MemfunctionBenchmarkDistribution(MemorySizeDistribution Distribution)
204d3c70d9fSGuillaume Chatelet       : Distribution(Distribution), Probabilities(Distribution.Probabilities),
205d3c70d9fSGuillaume Chatelet         SizeSampler(Probabilities.begin(), Probabilities.end()),
206d3c70d9fSGuillaume Chatelet         OffsetSampler(MemfunctionBenchmarkBase::BufferSize,
207d3c70d9fSGuillaume Chatelet                       Probabilities.size() - 1, MaybeAlign(AlignedAccess)) {}
208d3c70d9fSGuillaume Chatelet 
randomizellvm::libc_benchmarks::MemfunctionBenchmarkDistribution209d3c70d9fSGuillaume Chatelet   virtual void randomize() override {
210d3c70d9fSGuillaume Chatelet     for (auto &P : Parameters) {
211d3c70d9fSGuillaume Chatelet       P.OffsetBytes = OffsetSampler(Gen);
212d3c70d9fSGuillaume Chatelet       P.SizeBytes = SizeSampler(Gen);
213d3c70d9fSGuillaume Chatelet       checkValid(P);
214deae7e98SGuillaume Chatelet     }
215deae7e98SGuillaume Chatelet   }
216deae7e98SGuillaume Chatelet 
runllvm::libc_benchmarks::MemfunctionBenchmarkDistribution217d3c70d9fSGuillaume Chatelet   virtual Study run() override {
218d3c70d9fSGuillaume Chatelet     Study Study = createStudy();
219d3c70d9fSGuillaume Chatelet     Study.Configuration.SizeDistributionName = Distribution.Name.str();
220deae7e98SGuillaume Chatelet     BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions;
221deae7e98SGuillaume Chatelet     BO.MinDuration = std::chrono::milliseconds(10);
222d3c70d9fSGuillaume Chatelet     BO.InitialIterations = BatchSize * 10;
223d3c70d9fSGuillaume Chatelet     auto &Measurements = Study.Measurements;
224d3c70d9fSGuillaume Chatelet     Measurements.reserve(NumTrials);
225d3c70d9fSGuillaume Chatelet     runTrials(BO, Measurements);
226d3c70d9fSGuillaume Chatelet     return Study;
227deae7e98SGuillaume Chatelet   }
228d3c70d9fSGuillaume Chatelet 
229d3c70d9fSGuillaume Chatelet private:
230d3c70d9fSGuillaume Chatelet   MemorySizeDistribution Distribution;
231d3c70d9fSGuillaume Chatelet   ArrayRef<double> Probabilities;
232d3c70d9fSGuillaume Chatelet   std::discrete_distribution<unsigned> SizeSampler;
233d3c70d9fSGuillaume Chatelet   OffsetDistribution OffsetSampler;
234d3c70d9fSGuillaume Chatelet   std::mt19937_64 Gen;
235deae7e98SGuillaume Chatelet };
236deae7e98SGuillaume Chatelet 
writeStudy(const Study & S)237deae7e98SGuillaume Chatelet void writeStudy(const Study &S) {
238438f7fc0SSiva Chandra Reddy   std::error_code EC;
239438f7fc0SSiva Chandra Reddy   raw_fd_ostream FOS(Output, EC);
240438f7fc0SSiva Chandra Reddy   if (EC)
241438f7fc0SSiva Chandra Reddy     report_fatal_error(Twine("Could not open file: ")
242438f7fc0SSiva Chandra Reddy                            .concat(EC.message())
243438f7fc0SSiva Chandra Reddy                            .concat(", ")
244438f7fc0SSiva Chandra Reddy                            .concat(Output));
245438f7fc0SSiva Chandra Reddy   json::OStream JOS(FOS);
246deae7e98SGuillaume Chatelet   serializeToJson(S, JOS);
247ab577807SGuillaume Chatelet   FOS << "\n";
248deae7e98SGuillaume Chatelet }
249deae7e98SGuillaume Chatelet 
main()250deae7e98SGuillaume Chatelet void main() {
251deae7e98SGuillaume Chatelet   checkRequirements();
252d3c70d9fSGuillaume Chatelet   if (!isPowerOf2_32(AlignedAccess))
253d3c70d9fSGuillaume Chatelet     report_fatal_error(AlignedAccess.ArgStr +
254d3c70d9fSGuillaume Chatelet                        Twine(" must be a power of two or zero"));
255d3c70d9fSGuillaume Chatelet 
256d3c70d9fSGuillaume Chatelet   const bool HasDistributionName = !SizeDistributionName.empty();
257d3c70d9fSGuillaume Chatelet   if (SweepMode && HasDistributionName)
258d3c70d9fSGuillaume Chatelet     report_fatal_error("Select only one of `--" + Twine(SweepMode.ArgStr) +
259d3c70d9fSGuillaume Chatelet                        "` or `--" + Twine(SizeDistributionName.ArgStr) + "`");
260d3c70d9fSGuillaume Chatelet 
261d3c70d9fSGuillaume Chatelet   std::unique_ptr<MemfunctionBenchmarkBase> Benchmark;
262d3c70d9fSGuillaume Chatelet   if (SweepMode)
263d3c70d9fSGuillaume Chatelet     Benchmark.reset(new MemfunctionBenchmarkSweep());
264d3c70d9fSGuillaume Chatelet   else
265d3c70d9fSGuillaume Chatelet     Benchmark.reset(new MemfunctionBenchmarkDistribution(getDistributionOrDie(
2662f002817SAndre Vieira         BenchmarkSetup::getDistributions(), SizeDistributionName)));
267d3c70d9fSGuillaume Chatelet   writeStudy(Benchmark->run());
268438f7fc0SSiva Chandra Reddy }
269438f7fc0SSiva Chandra Reddy 
270438f7fc0SSiva Chandra Reddy } // namespace libc_benchmarks
271438f7fc0SSiva Chandra Reddy } // namespace llvm
272438f7fc0SSiva Chandra Reddy 
273d3c70d9fSGuillaume Chatelet #ifndef NDEBUG
274d3c70d9fSGuillaume Chatelet #error For reproducibility benchmarks should not be compiled in DEBUG mode.
275d3c70d9fSGuillaume Chatelet #endif
276d3c70d9fSGuillaume Chatelet 
main(int argc,char ** argv)277438f7fc0SSiva Chandra Reddy int main(int argc, char **argv) {
278438f7fc0SSiva Chandra Reddy   llvm::cl::ParseCommandLineOptions(argc, argv);
279deae7e98SGuillaume Chatelet   llvm::libc_benchmarks::main();
280438f7fc0SSiva Chandra Reddy   return EXIT_SUCCESS;
281438f7fc0SSiva Chandra Reddy }
282