1 //===-- Benchmark ---------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 #include "LibcBenchmark.h"
11 #include "LibcMemoryBenchmark.h"
12 #include "MemorySizeDistributions.h"
13 #include "llvm/Support/CommandLine.h"
14 #include "llvm/Support/ErrorHandling.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/JSON.h"
17 #include "llvm/Support/MathExtras.h"
18 #include "llvm/Support/MemoryBuffer.h"
19 #include "llvm/Support/raw_ostream.h"
24 namespace __llvm_libc
{
26 extern void *memcpy(void *__restrict
, const void *__restrict
, size_t);
27 extern void *memmove(void *, const void *, size_t);
28 extern void *memset(void *, int, size_t);
29 extern void bzero(void *, size_t);
30 extern int memcmp(const void *, const void *, size_t);
31 extern int bcmp(const void *, const void *, size_t);
33 } // namespace __llvm_libc
36 namespace libc_benchmarks
{
38 static cl::opt
<std::string
>
39 StudyName("study-name", cl::desc("The name for this study"), cl::Required
);
41 static cl::opt
<std::string
>
42 SizeDistributionName("size-distribution-name",
43 cl::desc("The name of the distribution to use"));
46 SweepMode("sweep-mode",
47 cl::desc("If set, benchmark all sizes from 0 to sweep-max-size"));
49 static cl::opt
<uint32_t>
50 SweepMaxSize("sweep-max-size",
51 cl::desc("The maximum size to use in sweep-mode"),
54 static cl::opt
<uint32_t>
55 AlignedAccess("aligned-access",
56 cl::desc("The alignment to use when accessing the buffers\n"
57 "Default is unaligned\n"
58 "Use 0 to disable address randomization"),
61 static cl::opt
<std::string
> Output("output",
62 cl::desc("Specify output filename"),
63 cl::value_desc("filename"), cl::init("-"));
65 static cl::opt
<uint32_t>
66 NumTrials("num-trials", cl::desc("The number of benchmarks run to perform"),
69 #if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY)
70 #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCPY
71 using BenchmarkSetup
= CopySetup
;
72 #elif defined(LIBC_BENCHMARK_FUNCTION_MEMMOVE)
73 #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMMOVE
74 using BenchmarkSetup
= MoveSetup
;
75 #elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET)
76 #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMSET
77 using BenchmarkSetup
= SetSetup
;
78 #elif defined(LIBC_BENCHMARK_FUNCTION_BZERO)
79 #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BZERO
80 using BenchmarkSetup
= SetSetup
;
81 #elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP)
82 #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP
83 using BenchmarkSetup
= ComparisonSetup
;
84 #elif defined(LIBC_BENCHMARK_FUNCTION_BCMP)
85 #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BCMP
86 using BenchmarkSetup
= ComparisonSetup
;
88 #error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition"
91 struct MemfunctionBenchmarkBase
: public BenchmarkSetup
{
92 MemfunctionBenchmarkBase() : ReportProgress(isatty(fileno(stdout
))) {}
93 virtual ~MemfunctionBenchmarkBase() {}
95 virtual Study
run() = 0;
97 CircularArrayRef
<ParameterBatch::ParameterType
>
98 generateBatch(size_t Iterations
) {
100 return cycle(ArrayRef(Parameters
), Iterations
);
104 Study
createStudy() {
107 Study
.StudyName
= StudyName
;
108 Runtime
&RI
= Study
.Runtime
;
109 RI
.Host
= HostState::get();
110 RI
.BufferSize
= BufferSize
;
111 RI
.BatchParameterCount
= BatchSize
;
113 BenchmarkOptions
&BO
= RI
.BenchmarkOptions
;
114 BO
.MinDuration
= std::chrono::milliseconds(1);
115 BO
.MaxDuration
= std::chrono::seconds(1);
116 BO
.MaxIterations
= 10'000'000U;
118 BO
.MaxSamples
= 1000;
119 BO
.Epsilon
= 0.01; // 1%
120 BO
.ScalingFactor
= 1.4;
122 StudyConfiguration
&SC
= Study
.Configuration
;
123 SC
.NumTrials
= NumTrials
;
124 SC
.IsSweepMode
= SweepMode
;
125 SC
.AccessAlignment
= MaybeAlign(AlignedAccess
);
126 SC
.Function
= LIBC_BENCHMARK_FUNCTION_NAME
;
130 void runTrials(const BenchmarkOptions
&Options
,
131 std::vector
<Duration
> &Measurements
) {
132 for (size_t i
= 0; i
< NumTrials
; ++i
) {
133 const BenchmarkResult Result
= benchmark(
134 Options
, *this, [this](ParameterBatch::ParameterType Parameter
) {
135 return Call(Parameter
, LIBC_BENCHMARK_FUNCTION
);
137 Measurements
.push_back(Result
.BestGuess
);
138 reportProgress(Measurements
);
142 virtual void randomize() = 0;
147 void reportProgress(const std::vector
<Duration
> &Measurements
) {
150 static size_t LastPercent
= -1;
151 const size_t TotalSteps
= Measurements
.capacity();
152 const size_t Steps
= Measurements
.size();
153 const size_t Percent
= 100 * Steps
/ TotalSteps
;
154 if (Percent
== LastPercent
)
156 LastPercent
= Percent
;
159 for (; I
<= Percent
; ++I
)
161 for (; I
<= 100; ++I
)
163 errs() << "] " << Percent
<< '%' << '\r';
167 struct MemfunctionBenchmarkSweep final
: public MemfunctionBenchmarkBase
{
168 MemfunctionBenchmarkSweep()
169 : OffsetSampler(MemfunctionBenchmarkBase::BufferSize
, SweepMaxSize
,
170 MaybeAlign(AlignedAccess
)) {}
172 virtual void randomize() override
{
173 for (auto &P
: Parameters
) {
174 P
.OffsetBytes
= OffsetSampler(Gen
);
175 P
.SizeBytes
= CurrentSweepSize
;
180 virtual Study
run() override
{
181 Study Study
= createStudy();
182 Study
.Configuration
.SweepModeMaxSize
= SweepMaxSize
;
183 BenchmarkOptions
&BO
= Study
.Runtime
.BenchmarkOptions
;
184 BO
.MinDuration
= std::chrono::milliseconds(1);
185 BO
.InitialIterations
= 100;
186 auto &Measurements
= Study
.Measurements
;
187 Measurements
.reserve(NumTrials
* SweepMaxSize
);
188 for (size_t Size
= 0; Size
<= SweepMaxSize
; ++Size
) {
189 CurrentSweepSize
= Size
;
190 runTrials(BO
, Measurements
);
196 size_t CurrentSweepSize
= 0;
197 OffsetDistribution OffsetSampler
;
201 struct MemfunctionBenchmarkDistribution final
202 : public MemfunctionBenchmarkBase
{
203 MemfunctionBenchmarkDistribution(MemorySizeDistribution Distribution
)
204 : Distribution(Distribution
), Probabilities(Distribution
.Probabilities
),
205 SizeSampler(Probabilities
.begin(), Probabilities
.end()),
206 OffsetSampler(MemfunctionBenchmarkBase::BufferSize
,
207 Probabilities
.size() - 1, MaybeAlign(AlignedAccess
)) {}
209 virtual void randomize() override
{
210 for (auto &P
: Parameters
) {
211 P
.OffsetBytes
= OffsetSampler(Gen
);
212 P
.SizeBytes
= SizeSampler(Gen
);
217 virtual Study
run() override
{
218 Study Study
= createStudy();
219 Study
.Configuration
.SizeDistributionName
= Distribution
.Name
.str();
220 BenchmarkOptions
&BO
= Study
.Runtime
.BenchmarkOptions
;
221 BO
.MinDuration
= std::chrono::milliseconds(10);
222 BO
.InitialIterations
= BatchSize
* 10;
223 auto &Measurements
= Study
.Measurements
;
224 Measurements
.reserve(NumTrials
);
225 runTrials(BO
, Measurements
);
230 MemorySizeDistribution Distribution
;
231 ArrayRef
<double> Probabilities
;
232 std::discrete_distribution
<unsigned> SizeSampler
;
233 OffsetDistribution OffsetSampler
;
237 void writeStudy(const Study
&S
) {
239 raw_fd_ostream
FOS(Output
, EC
);
241 report_fatal_error(Twine("Could not open file: ")
242 .concat(EC
.message())
245 json::OStream
JOS(FOS
);
246 serializeToJson(S
, JOS
);
252 if (!isPowerOf2_32(AlignedAccess
))
253 report_fatal_error(AlignedAccess
.ArgStr
+
254 Twine(" must be a power of two or zero"));
256 const bool HasDistributionName
= !SizeDistributionName
.empty();
257 if (SweepMode
&& HasDistributionName
)
258 report_fatal_error("Select only one of `--" + Twine(SweepMode
.ArgStr
) +
259 "` or `--" + Twine(SizeDistributionName
.ArgStr
) + "`");
261 std::unique_ptr
<MemfunctionBenchmarkBase
> Benchmark
;
263 Benchmark
.reset(new MemfunctionBenchmarkSweep());
265 Benchmark
.reset(new MemfunctionBenchmarkDistribution(getDistributionOrDie(
266 BenchmarkSetup::getDistributions(), SizeDistributionName
)));
267 writeStudy(Benchmark
->run());
270 } // namespace libc_benchmarks
274 #error For reproducibility benchmarks should not be compiled in DEBUG mode.
277 int main(int argc
, char **argv
) {
278 llvm::cl::ParseCommandLineOptions(argc
, argv
);
279 llvm::libc_benchmarks::main();