llvm-project
287 строк · 9.2 Кб
1//===-- Benchmark ---------------------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "JSON.h"
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"
20
21#include <cstring>
22#include <unistd.h>
23
24namespace LIBC_NAMESPACE {
25
26extern void *memcpy(void *__restrict, const void *__restrict, size_t);
27extern void *memmove(void *, const void *, size_t);
28extern void *memset(void *, int, size_t);
29extern void bzero(void *, size_t);
30extern int memcmp(const void *, const void *, size_t);
31extern int bcmp(const void *, const void *, size_t);
32
33} // namespace LIBC_NAMESPACE
34
35namespace llvm {
36namespace libc_benchmarks {
37
38static cl::opt<std::string>
39StudyName("study-name", cl::desc("The name for this study"), cl::Required);
40
41static cl::opt<std::string>
42SizeDistributionName("size-distribution-name",
43cl::desc("The name of the distribution to use"));
44
45static cl::opt<bool> SweepMode(
46"sweep-mode",
47cl::desc(
48"If set, benchmark all sizes from sweep-min-size to sweep-max-size"));
49
50static cl::opt<uint32_t>
51SweepMinSize("sweep-min-size",
52cl::desc("The minimum size to use in sweep-mode"),
53cl::init(0));
54
55static cl::opt<uint32_t>
56SweepMaxSize("sweep-max-size",
57cl::desc("The maximum size to use in sweep-mode"),
58cl::init(256));
59
60static cl::opt<uint32_t>
61AlignedAccess("aligned-access",
62cl::desc("The alignment to use when accessing the buffers\n"
63"Default is unaligned\n"
64"Use 0 to disable address randomization"),
65cl::init(1));
66
67static cl::opt<std::string> Output("output",
68cl::desc("Specify output filename"),
69cl::value_desc("filename"), cl::init("-"));
70
71static cl::opt<uint32_t>
72NumTrials("num-trials", cl::desc("The number of benchmarks run to perform"),
73cl::init(1));
74
75#if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY)
76#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCPY
77using BenchmarkSetup = CopySetup;
78#elif defined(LIBC_BENCHMARK_FUNCTION_MEMMOVE)
79#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMMOVE
80using BenchmarkSetup = MoveSetup;
81#elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET)
82#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMSET
83using BenchmarkSetup = SetSetup;
84#elif defined(LIBC_BENCHMARK_FUNCTION_BZERO)
85#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BZERO
86using BenchmarkSetup = SetSetup;
87#elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP)
88#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP
89using BenchmarkSetup = ComparisonSetup;
90#elif defined(LIBC_BENCHMARK_FUNCTION_BCMP)
91#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BCMP
92using BenchmarkSetup = ComparisonSetup;
93#else
94#error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition"
95#endif
96
97struct MemfunctionBenchmarkBase : public BenchmarkSetup {
98MemfunctionBenchmarkBase() : ReportProgress(isatty(fileno(stdout))) {}
99virtual ~MemfunctionBenchmarkBase() {}
100
101virtual Study run() = 0;
102
103CircularArrayRef<ParameterBatch::ParameterType>
104generateBatch(size_t Iterations) {
105randomize();
106return cycle(ArrayRef(Parameters), Iterations);
107}
108
109protected:
110Study createStudy() {
111Study Study;
112// Setup study.
113Study.StudyName = StudyName;
114Runtime &RI = Study.Runtime;
115RI.Host = HostState::get();
116RI.BufferSize = BufferSize;
117RI.BatchParameterCount = BatchSize;
118
119BenchmarkOptions &BO = RI.BenchmarkOptions;
120BO.MinDuration = std::chrono::milliseconds(1);
121BO.MaxDuration = std::chrono::seconds(1);
122BO.MaxIterations = 10'000'000U;
123BO.MinSamples = 4;
124BO.MaxSamples = 1000;
125BO.Epsilon = 0.01; // 1%
126BO.ScalingFactor = 1.4;
127
128StudyConfiguration &SC = Study.Configuration;
129SC.NumTrials = NumTrials;
130SC.IsSweepMode = SweepMode;
131SC.AccessAlignment = MaybeAlign(AlignedAccess);
132SC.Function = LIBC_BENCHMARK_FUNCTION_NAME;
133return Study;
134}
135
136void runTrials(const BenchmarkOptions &Options,
137std::vector<Duration> &Measurements) {
138for (size_t i = 0; i < NumTrials; ++i) {
139const BenchmarkResult Result = benchmark(
140Options, *this, [this](ParameterBatch::ParameterType Parameter) {
141return Call(Parameter, LIBC_BENCHMARK_FUNCTION);
142});
143Measurements.push_back(Result.BestGuess);
144reportProgress(Measurements);
145}
146}
147
148virtual void randomize() = 0;
149
150private:
151bool ReportProgress;
152
153void reportProgress(const std::vector<Duration> &Measurements) {
154if (!ReportProgress)
155return;
156static size_t LastPercent = -1;
157const size_t TotalSteps = Measurements.capacity();
158const size_t Steps = Measurements.size();
159const size_t Percent = 100 * Steps / TotalSteps;
160if (Percent == LastPercent)
161return;
162LastPercent = Percent;
163size_t I = 0;
164errs() << '[';
165for (; I <= Percent; ++I)
166errs() << '#';
167for (; I <= 100; ++I)
168errs() << '_';
169errs() << "] " << Percent << '%' << '\r';
170}
171};
172
173struct MemfunctionBenchmarkSweep final : public MemfunctionBenchmarkBase {
174MemfunctionBenchmarkSweep()
175: OffsetSampler(MemfunctionBenchmarkBase::BufferSize, SweepMaxSize,
176MaybeAlign(AlignedAccess)) {}
177
178virtual void randomize() override {
179for (auto &P : Parameters) {
180P.OffsetBytes = OffsetSampler(Gen);
181P.SizeBytes = CurrentSweepSize;
182checkValid(P);
183}
184}
185
186virtual Study run() override {
187Study Study = createStudy();
188Study.Configuration.SweepModeMaxSize = SweepMaxSize;
189BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions;
190BO.MinDuration = std::chrono::milliseconds(1);
191BO.InitialIterations = 100;
192auto &Measurements = Study.Measurements;
193Measurements.reserve(NumTrials * SweepMaxSize);
194for (size_t Size = SweepMinSize; Size <= SweepMaxSize; ++Size) {
195CurrentSweepSize = Size;
196runTrials(BO, Measurements);
197}
198return Study;
199}
200
201private:
202size_t CurrentSweepSize = 0;
203OffsetDistribution OffsetSampler;
204std::mt19937_64 Gen;
205};
206
207struct MemfunctionBenchmarkDistribution final
208: public MemfunctionBenchmarkBase {
209MemfunctionBenchmarkDistribution(MemorySizeDistribution Distribution)
210: Distribution(Distribution), Probabilities(Distribution.Probabilities),
211SizeSampler(Probabilities.begin(), Probabilities.end()),
212OffsetSampler(MemfunctionBenchmarkBase::BufferSize,
213Probabilities.size() - 1, MaybeAlign(AlignedAccess)) {}
214
215virtual void randomize() override {
216for (auto &P : Parameters) {
217P.OffsetBytes = OffsetSampler(Gen);
218P.SizeBytes = SizeSampler(Gen);
219checkValid(P);
220}
221}
222
223virtual Study run() override {
224Study Study = createStudy();
225Study.Configuration.SizeDistributionName = Distribution.Name.str();
226BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions;
227BO.MinDuration = std::chrono::milliseconds(10);
228BO.InitialIterations = BatchSize * 10;
229auto &Measurements = Study.Measurements;
230Measurements.reserve(NumTrials);
231runTrials(BO, Measurements);
232return Study;
233}
234
235private:
236MemorySizeDistribution Distribution;
237ArrayRef<double> Probabilities;
238std::discrete_distribution<unsigned> SizeSampler;
239OffsetDistribution OffsetSampler;
240std::mt19937_64 Gen;
241};
242
243void writeStudy(const Study &S) {
244std::error_code EC;
245raw_fd_ostream FOS(Output, EC);
246if (EC)
247report_fatal_error(Twine("Could not open file: ")
248.concat(EC.message())
249.concat(", ")
250.concat(Output));
251json::OStream JOS(FOS);
252serializeToJson(S, JOS);
253FOS << "\n";
254}
255
256void main() {
257checkRequirements();
258if (!isPowerOf2_32(AlignedAccess))
259report_fatal_error(AlignedAccess.ArgStr +
260Twine(" must be a power of two or zero"));
261
262const bool HasDistributionName = !SizeDistributionName.empty();
263if (SweepMode && HasDistributionName)
264report_fatal_error("Select only one of `--" + Twine(SweepMode.ArgStr) +
265"` or `--" + Twine(SizeDistributionName.ArgStr) + "`");
266
267std::unique_ptr<MemfunctionBenchmarkBase> Benchmark;
268if (SweepMode)
269Benchmark.reset(new MemfunctionBenchmarkSweep());
270else
271Benchmark.reset(new MemfunctionBenchmarkDistribution(getDistributionOrDie(
272BenchmarkSetup::getDistributions(), SizeDistributionName)));
273writeStudy(Benchmark->run());
274}
275
276} // namespace libc_benchmarks
277} // namespace llvm
278
279#ifndef NDEBUG
280#error For reproducibility benchmarks should not be compiled in DEBUG mode.
281#endif
282
283int main(int argc, char **argv) {
284llvm::cl::ParseCommandLineOptions(argc, argv);
285llvm::libc_benchmarks::main();
286return EXIT_SUCCESS;
287}
288