diff --git a/libc/benchmarks/CMakeLists.txt b/libc/benchmarks/CMakeLists.txt index 390b802bd8f7..71dbf836e50d 100644 --- a/libc/benchmarks/CMakeLists.txt +++ b/libc/benchmarks/CMakeLists.txt @@ -160,7 +160,7 @@ function(add_libc_multi_impl_benchmark name) get_target_property(entrypoint_object_file ${fq_config_name} "OBJECT_FILE_RAW") target_link_libraries(${benchmark_name} PUBLIC json ${entrypoint_object_file}) string(TOUPPER ${name} name_upper) - target_compile_definitions(${benchmark_name} PRIVATE "-DLIBC_BENCHMARK_FUNCTION_${name_upper}=1" "-DLIBC_BENCHMARK_FUNCTION_NAME=\"${fq_config_name}\"") + target_compile_definitions(${benchmark_name} PRIVATE "-DLIBC_BENCHMARK_FUNCTION_${name_upper}=__llvm_libc::${name}" "-DLIBC_BENCHMARK_FUNCTION_NAME=\"${fq_config_name}\"") else() message(STATUS "Skipping benchmark for '${fq_config_name}' insufficient host cpu features '${required_cpu_features}'") endif() diff --git a/libc/benchmarks/LibcMemoryBenchmark.cpp b/libc/benchmarks/LibcMemoryBenchmark.cpp index 05f5ff520484..37173a98189e 100644 --- a/libc/benchmarks/LibcMemoryBenchmark.cpp +++ b/libc/benchmarks/LibcMemoryBenchmark.cpp @@ -8,6 +8,7 @@ #include "LibcMemoryBenchmark.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include @@ -60,5 +61,70 @@ MismatchOffsetDistribution::MismatchOffsetDistribution(size_t BufferSize, std::uniform_int_distribution(0, MismatchIndices.size() - 1); } +static size_t getL1DataCacheSize() { + const std::vector &CacheInfos = HostState::get().Caches; + const auto IsL1DataCache = [](const CacheInfo &CI) { + return CI.Type == "Data" && CI.Level == 1; + }; + const auto CacheIt = find_if(CacheInfos, IsL1DataCache); + if (CacheIt != CacheInfos.end()) + return CacheIt->Size; + report_fatal_error("Unable to read L1 Cache Data Size"); +} + +static size_t getAvailableBufferSize() { + static constexpr int64_t KiB = 1024; + static constexpr int64_t ParameterStorageBytes = 4 * KiB; + static constexpr int64_t L1LeftAsideBytes = 1 * KiB; + return getL1DataCacheSize() - L1LeftAsideBytes - ParameterStorageBytes; +} + +ParameterBatch::ParameterBatch(size_t BufferCount) + : BufferSize(getAvailableBufferSize() / BufferCount), + BatchSize(BufferSize / sizeof(ParameterType)), Parameters(BatchSize) { + if (BufferSize <= 0 || BatchSize < 100) + report_fatal_error("Not enough L1 cache"); +} + +size_t ParameterBatch::getBatchBytes() const { + size_t BatchBytes = 0; + for (auto &P : Parameters) + BatchBytes += P.SizeBytes; + return BatchBytes; +} + +void ParameterBatch::checkValid(const ParameterType &P) const { + if (P.OffsetBytes + P.SizeBytes >= BufferSize) + report_fatal_error( + llvm::Twine("Call would result in buffer overflow: Offset=") + .concat(llvm::Twine(P.OffsetBytes)) + .concat(", Size=") + .concat(llvm::Twine(P.SizeBytes)) + .concat(", BufferSize=") + .concat(llvm::Twine(BufferSize))); +} + +const ArrayRef CopyHarness::Distributions = + getMemcpySizeDistributions(); +const ArrayRef ComparisonHarness::Distributions = + getMemcmpSizeDistributions(); +const ArrayRef SetHarness::Distributions = + getMemsetSizeDistributions(); + +CopyHarness::CopyHarness() + : ParameterBatch(2), SrcBuffer(ParameterBatch::BufferSize), + DstBuffer(ParameterBatch::BufferSize) {} + +ComparisonHarness::ComparisonHarness() + : ParameterBatch(2), LhsBuffer(ParameterBatch::BufferSize), + RhsBuffer(ParameterBatch::BufferSize) { + // The memcmp buffers always compare equal. + memset(LhsBuffer.begin(), 0xF, BufferSize); + memset(RhsBuffer.begin(), 0xF, BufferSize); +} + +SetHarness::SetHarness() + : ParameterBatch(1), DstBuffer(ParameterBatch::BufferSize) {} + } // namespace libc_benchmarks } // namespace llvm diff --git a/libc/benchmarks/LibcMemoryBenchmark.h b/libc/benchmarks/LibcMemoryBenchmark.h index fc66990999f6..319e2d85811b 100644 --- a/libc/benchmarks/LibcMemoryBenchmark.h +++ b/libc/benchmarks/LibcMemoryBenchmark.h @@ -13,6 +13,7 @@ #define LLVM_LIBC_UTILS_BENCHMARK_MEMORY_BENCHMARK_H #include "LibcBenchmark.h" +#include "MemorySizeDistributions.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Alignment.h" #include @@ -162,6 +163,87 @@ public: } }; +/// This structure holds a vector of ParameterType. +/// It makes sure that BufferCount x BufferSize Bytes and the vector of +/// ParameterType can all fit in the L1 cache. +struct ParameterBatch { + struct ParameterType { + unsigned OffsetBytes : 16; // max : 16 KiB - 1 + unsigned SizeBytes : 16; // max : 16 KiB - 1 + }; + + ParameterBatch(size_t BufferCount); + + /// Verifies that memory accessed through this parameter is valid. + void checkValid(const ParameterType &) const; + + /// Computes the number of bytes processed during within this batch. + size_t getBatchBytes() const; + + const size_t BufferSize; + const size_t BatchSize; + std::vector Parameters; +}; + +/// Provides source and destination buffers for the Copy operation as well as +/// the associated size distributions. +struct CopyHarness : public ParameterBatch { + CopyHarness(); + + static const ArrayRef Distributions; + + inline void *Call(ParameterType Parameter, + void *(*memcpy)(void *__restrict, const void *__restrict, + size_t)) { + return memcpy(DstBuffer + Parameter.OffsetBytes, + SrcBuffer + Parameter.OffsetBytes, Parameter.SizeBytes); + } + +private: + AlignedBuffer SrcBuffer; + AlignedBuffer DstBuffer; +}; + +/// Provides destination buffer for the Set operation as well as the associated +/// size distributions. +struct SetHarness : public ParameterBatch { + SetHarness(); + + static const ArrayRef Distributions; + + inline void *Call(ParameterType Parameter, + void *(*memset)(void *, int, size_t)) { + return memset(DstBuffer + Parameter.OffsetBytes, + Parameter.OffsetBytes % 0xFF, Parameter.SizeBytes); + } + + inline void *Call(ParameterType Parameter, void (*bzero)(void *, size_t)) { + bzero(DstBuffer + Parameter.OffsetBytes, Parameter.SizeBytes); + return DstBuffer.begin(); + } + +private: + AlignedBuffer DstBuffer; +}; + +/// Provides left and right buffers for the Comparison operation as well as the +/// associated size distributions. +struct ComparisonHarness : public ParameterBatch { + ComparisonHarness(); + + static const ArrayRef Distributions; + + inline int Call(ParameterType Parameter, + int (*memcmp)(const void *, const void *, size_t)) { + return memcmp(LhsBuffer + Parameter.OffsetBytes, + RhsBuffer + Parameter.OffsetBytes, Parameter.SizeBytes); + } + +private: + AlignedBuffer LhsBuffer; + AlignedBuffer RhsBuffer; +}; + } // namespace libc_benchmarks } // namespace llvm diff --git a/libc/benchmarks/LibcMemoryBenchmarkMain.cpp b/libc/benchmarks/LibcMemoryBenchmarkMain.cpp index 6fa01ede52e3..d7e1c1f425da 100644 --- a/libc/benchmarks/LibcMemoryBenchmarkMain.cpp +++ b/libc/benchmarks/LibcMemoryBenchmarkMain.cpp @@ -14,10 +14,12 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include +#include namespace __llvm_libc { @@ -62,172 +64,43 @@ static cl::opt NumTrials("num-trials", cl::desc("The number of benchmarks run to perform"), cl::init(1)); -static constexpr int64_t KiB = 1024; -static constexpr int64_t ParameterStorageBytes = 4 * KiB; -static constexpr int64_t L1LeftAsideBytes = 1 * KiB; - -struct ParameterType { - unsigned OffsetBytes : 16; // max : 16 KiB - 1 - unsigned SizeBytes : 16; // max : 16 KiB - 1 -}; - #if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY) -struct Benchmark { - static constexpr auto GetDistributions = &getMemcpySizeDistributions; - static constexpr size_t BufferCount = 2; - - Benchmark(const size_t BufferSize) - : SrcBuffer(BufferSize), DstBuffer(BufferSize) {} - - inline auto functor() { - return [this](ParameterType P) { - __llvm_libc::memcpy(DstBuffer + P.OffsetBytes, SrcBuffer + P.OffsetBytes, - P.SizeBytes); - return DstBuffer[P.OffsetBytes]; - }; - } - - AlignedBuffer SrcBuffer; - AlignedBuffer DstBuffer; -}; +#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCPY +using BenchmarkHarness = CopyHarness; #elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET) -struct Benchmark { - static constexpr auto GetDistributions = &getMemsetSizeDistributions; - static constexpr size_t BufferCount = 1; - - Benchmark(const size_t BufferSize) : DstBuffer(BufferSize) {} - - inline auto functor() { - return [this](ParameterType P) { - __llvm_libc::memset(DstBuffer + P.OffsetBytes, P.OffsetBytes & 0xFF, - P.SizeBytes); - return DstBuffer[P.OffsetBytes]; - }; - } - - AlignedBuffer DstBuffer; -}; +#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMSET +using BenchmarkHarness = SetHarness; #elif defined(LIBC_BENCHMARK_FUNCTION_BZERO) -struct Benchmark { - static constexpr auto GetDistributions = &getMemsetSizeDistributions; - static constexpr size_t BufferCount = 1; - - Benchmark(const size_t BufferSize) : DstBuffer(BufferSize) {} - - inline auto functor() { - return [this](ParameterType P) { - __llvm_libc::bzero(DstBuffer + P.OffsetBytes, P.SizeBytes); - return DstBuffer[P.OffsetBytes]; - }; - } - - AlignedBuffer DstBuffer; -}; +#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BZERO +using BenchmarkHarness = SetHarness; #elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP) -struct Benchmark { - static constexpr auto GetDistributions = &getMemcmpSizeDistributions; - static constexpr size_t BufferCount = 2; - - Benchmark(const size_t BufferSize) - : BufferA(BufferSize), BufferB(BufferSize) { - // The memcmp buffers always compare equal. - memset(BufferA.begin(), 0xF, BufferSize); - memset(BufferB.begin(), 0xF, BufferSize); - } - - inline auto functor() { - return [this](ParameterType P) { - return __llvm_libc::memcmp(BufferA + P.OffsetBytes, - BufferB + P.OffsetBytes, P.SizeBytes); - }; - } - - AlignedBuffer BufferA; - AlignedBuffer BufferB; -}; +#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP +using BenchmarkHarness = ComparisonHarness; #else #error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition" #endif -struct Harness : Benchmark { - Harness(const size_t BufferSize, size_t BatchParameterCount, - std::function SizeSampler, - std::function OffsetSampler) - : Benchmark(BufferSize), BufferSize(BufferSize), - Parameters(BatchParameterCount), SizeSampler(SizeSampler), - OffsetSampler(OffsetSampler) {} +struct MemfunctionBenchmarkBase : public BenchmarkHarness { + MemfunctionBenchmarkBase() : ReportProgress(isatty(fileno(stdout))) {} + virtual ~MemfunctionBenchmarkBase() {} - CircularArrayRef generateBatch(size_t Iterations) { - for (auto &P : Parameters) { - P.OffsetBytes = OffsetSampler(); - P.SizeBytes = SizeSampler(); - if (P.OffsetBytes + P.SizeBytes >= BufferSize) - report_fatal_error("Call would result in buffer overflow"); - } + virtual Study run() = 0; + + CircularArrayRef + generateBatch(size_t Iterations) { + randomize(); return cycle(makeArrayRef(Parameters), Iterations); } -private: - const size_t BufferSize; - std::vector Parameters; - std::function SizeSampler; - std::function OffsetSampler; -}; - -size_t getL1DataCacheSize() { - const std::vector &CacheInfos = HostState::get().Caches; - const auto IsL1DataCache = [](const CacheInfo &CI) { - return CI.Type == "Data" && CI.Level == 1; - }; - const auto CacheIt = find_if(CacheInfos, IsL1DataCache); - if (CacheIt != CacheInfos.end()) - return CacheIt->Size; - report_fatal_error("Unable to read L1 Cache Data Size"); -} - -struct MemfunctionBenchmark { - MemfunctionBenchmark(int64_t L1Size = getL1DataCacheSize()) - : AvailableSize(L1Size - L1LeftAsideBytes - ParameterStorageBytes), - BufferSize(AvailableSize / Benchmark::BufferCount), - BatchParameterCount(BufferSize / sizeof(ParameterType)) { - // Handling command line flags - if (AvailableSize <= 0 || BufferSize <= 0 || BatchParameterCount < 100) - report_fatal_error("Not enough L1 cache"); - - if (!isPowerOfTwoOrZero(AlignedAccess)) - report_fatal_error(AlignedAccess.ArgStr + - Twine(" must be a power of two or zero")); - - const bool HasDistributionName = !SizeDistributionName.empty(); - if (SweepMode && HasDistributionName) - report_fatal_error("Select only one of `--" + Twine(SweepMode.ArgStr) + - "` or `--" + Twine(SizeDistributionName.ArgStr) + "`"); - - if (SweepMode) { - MaxSizeValue = SweepMaxSize; - } else { - std::map Map; - for (MemorySizeDistribution Distribution : Benchmark::GetDistributions()) - Map[Distribution.Name] = Distribution; - if (Map.count(SizeDistributionName) == 0) { - std::string Message; - raw_string_ostream Stream(Message); - Stream << "Unknown --" << SizeDistributionName.ArgStr << "='" - << SizeDistributionName << "', available distributions:\n"; - for (const auto &Pair : Map) - Stream << "'" << Pair.first << "'\n"; - report_fatal_error(Stream.str()); - } - SizeDistribution = Map[SizeDistributionName]; - MaxSizeValue = SizeDistribution.Probabilities.size() - 1; - } - - // Setup study. +protected: + Study createStudy() { + Study Study; + // Harness study. Study.StudyName = StudyName; Runtime &RI = Study.Runtime; RI.Host = HostState::get(); RI.BufferSize = BufferSize; - RI.BatchParameterCount = BatchParameterCount; + RI.BatchParameterCount = BatchSize; BenchmarkOptions &BO = RI.BenchmarkOptions; BO.MinDuration = std::chrono::milliseconds(1); @@ -241,56 +114,34 @@ struct MemfunctionBenchmark { StudyConfiguration &SC = Study.Configuration; SC.NumTrials = NumTrials; SC.IsSweepMode = SweepMode; - if (SweepMode) - SC.SweepModeMaxSize = SweepMaxSize; - else - SC.SizeDistributionName = SizeDistributionName; SC.AccessAlignment = MaybeAlign(AlignedAccess); SC.Function = LIBC_BENCHMARK_FUNCTION_NAME; - } - - Study run() { - if (SweepMode) - runSweepMode(); - else - runDistributionMode(); return Study; } + void runTrials(const BenchmarkOptions &Options, + std::vector &Measurements) { + for (size_t i = 0; i < NumTrials; ++i) { + const BenchmarkResult Result = benchmark( + Options, *this, [this](ParameterBatch::ParameterType Parameter) { + return Call(Parameter, LIBC_BENCHMARK_FUNCTION); + }); + Measurements.push_back(Result.BestGuess); + reportProgress(Measurements); + } + } + + virtual void randomize() = 0; + private: - const int64_t AvailableSize; - const int64_t BufferSize; - const size_t BatchParameterCount; - size_t MaxSizeValue = 0; - MemorySizeDistribution SizeDistribution; - Study Study; - std::mt19937_64 Gen; + bool ReportProgress; - static constexpr bool isPowerOfTwoOrZero(size_t Value) { - return (Value & (Value - 1U)) == 0; - } - - std::function geOffsetSampler() { - return [this]() { - static OffsetDistribution OD(BufferSize, MaxSizeValue, - Study.Configuration.AccessAlignment); - return OD(Gen); - }; - } - - std::function getSizeSampler() { - return [this]() { - static std::discrete_distribution Distribution( - SizeDistribution.Probabilities.begin(), - SizeDistribution.Probabilities.end()); - return Distribution(Gen); - }; - } - - void reportProgress() { + void reportProgress(const std::vector &Measurements) { + if (!ReportProgress) + return; static size_t LastPercent = -1; - const size_t TotalSteps = Study.Measurements.capacity(); - const size_t Steps = Study.Measurements.size(); + const size_t TotalSteps = Measurements.capacity(); + const size_t Steps = Measurements.size(); const size_t Percent = 100 * Steps / TotalSteps; if (Percent == LastPercent) return; @@ -303,40 +154,76 @@ private: errs() << '_'; errs() << "] " << Percent << '%' << '\r'; } +}; - void runTrials(const BenchmarkOptions &Options, - std::function SizeSampler, - std::function OffsetSampler) { - Harness B(BufferSize, BatchParameterCount, SizeSampler, OffsetSampler); - for (size_t i = 0; i < NumTrials; ++i) { - const BenchmarkResult Result = benchmark(Options, B, B.functor()); - Study.Measurements.push_back(Result.BestGuess); - reportProgress(); +struct MemfunctionBenchmarkSweep final : public MemfunctionBenchmarkBase { + MemfunctionBenchmarkSweep() + : OffsetSampler(MemfunctionBenchmarkBase::BufferSize, SweepMaxSize, + MaybeAlign(AlignedAccess)) {} + + virtual void randomize() override { + for (auto &P : Parameters) { + P.OffsetBytes = OffsetSampler(Gen); + P.SizeBytes = CurrentSweepSize; + checkValid(P); } } - void runSweepMode() { - Study.Measurements.reserve(NumTrials * SweepMaxSize); - + virtual Study run() override { + Study Study = createStudy(); + Study.Configuration.SweepModeMaxSize = SweepMaxSize; BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions; BO.MinDuration = std::chrono::milliseconds(1); BO.InitialIterations = 100; - + auto &Measurements = Study.Measurements; + Measurements.reserve(NumTrials * SweepMaxSize); for (size_t Size = 0; Size <= SweepMaxSize; ++Size) { - const auto SizeSampler = [Size]() { return Size; }; - runTrials(BO, SizeSampler, geOffsetSampler()); + CurrentSweepSize = Size; + runTrials(BO, Measurements); + } + return Study; + } + +private: + size_t CurrentSweepSize = 0; + OffsetDistribution OffsetSampler; + std::mt19937_64 Gen; +}; + +struct MemfunctionBenchmarkDistribution final + : public MemfunctionBenchmarkBase { + MemfunctionBenchmarkDistribution(MemorySizeDistribution Distribution) + : Distribution(Distribution), Probabilities(Distribution.Probabilities), + SizeSampler(Probabilities.begin(), Probabilities.end()), + OffsetSampler(MemfunctionBenchmarkBase::BufferSize, + Probabilities.size() - 1, MaybeAlign(AlignedAccess)) {} + + virtual void randomize() override { + for (auto &P : Parameters) { + P.OffsetBytes = OffsetSampler(Gen); + P.SizeBytes = SizeSampler(Gen); + checkValid(P); } } - void runDistributionMode() { - Study.Measurements.reserve(NumTrials); - + virtual Study run() override { + Study Study = createStudy(); + Study.Configuration.SizeDistributionName = Distribution.Name.str(); BenchmarkOptions &BO = Study.Runtime.BenchmarkOptions; BO.MinDuration = std::chrono::milliseconds(10); - BO.InitialIterations = BatchParameterCount * 10; - - runTrials(BO, getSizeSampler(), geOffsetSampler()); + BO.InitialIterations = BatchSize * 10; + auto &Measurements = Study.Measurements; + Measurements.reserve(NumTrials); + runTrials(BO, Measurements); + return Study; } + +private: + MemorySizeDistribution Distribution; + ArrayRef Probabilities; + std::discrete_distribution SizeSampler; + OffsetDistribution OffsetSampler; + std::mt19937_64 Gen; }; void writeStudy(const Study &S) { @@ -354,20 +241,33 @@ void writeStudy(const Study &S) { void main() { checkRequirements(); - MemfunctionBenchmark MB; - writeStudy(MB.run()); + if (!isPowerOf2_32(AlignedAccess)) + report_fatal_error(AlignedAccess.ArgStr + + Twine(" must be a power of two or zero")); + + const bool HasDistributionName = !SizeDistributionName.empty(); + if (SweepMode && HasDistributionName) + report_fatal_error("Select only one of `--" + Twine(SweepMode.ArgStr) + + "` or `--" + Twine(SizeDistributionName.ArgStr) + "`"); + + std::unique_ptr Benchmark; + if (SweepMode) + Benchmark.reset(new MemfunctionBenchmarkSweep()); + else + Benchmark.reset(new MemfunctionBenchmarkDistribution(getDistributionOrDie( + BenchmarkHarness::Distributions, SizeDistributionName))); + writeStudy(Benchmark->run()); } } // namespace libc_benchmarks } // namespace llvm +#ifndef NDEBUG +#error For reproducibility benchmarks should not be compiled in DEBUG mode. +#endif + int main(int argc, char **argv) { llvm::cl::ParseCommandLineOptions(argc, argv); -#ifndef NDEBUG - static_assert( - false, - "For reproducibility benchmarks should not be compiled in DEBUG mode."); -#endif llvm::libc_benchmarks::main(); return EXIT_SUCCESS; } diff --git a/libc/benchmarks/MemorySizeDistributions.cpp b/libc/benchmarks/MemorySizeDistributions.cpp index e46590dfe014..aa7e9a4083fc 100644 --- a/libc/benchmarks/MemorySizeDistributions.cpp +++ b/libc/benchmarks/MemorySizeDistributions.cpp @@ -1,5 +1,8 @@ #include "MemorySizeDistributions.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + namespace llvm { namespace libc_benchmarks { @@ -135,5 +138,24 @@ ArrayRef getMemcmpSizeDistributions() { }; return kDistributions; } + +MemorySizeDistribution +getDistributionOrDie(ArrayRef Distributions, + StringRef Name) { + size_t Index = 0; + for (const auto &MSD : Distributions) { + if (MSD.Name == Name) + return MSD; + ++Index; + } + std::string Message; + raw_string_ostream Stream(Message); + Stream << "Unknown MemorySizeDistribution '" << Name + << "', available distributions:\n"; + for (const auto &MSD : Distributions) + Stream << "'" << MSD.Name << "'\n"; + report_fatal_error(Stream.str()); +} + } // namespace libc_benchmarks } // namespace llvm diff --git a/libc/benchmarks/MemorySizeDistributions.h b/libc/benchmarks/MemorySizeDistributions.h index 3a25ae3aa073..c5a2768d4929 100644 --- a/libc/benchmarks/MemorySizeDistributions.h +++ b/libc/benchmarks/MemorySizeDistributions.h @@ -38,6 +38,12 @@ ArrayRef getMemsetSizeDistributions(); /// Returns a list of memcmp size distributions. ArrayRef getMemcmpSizeDistributions(); +/// Returns the first MemorySizeDistribution from Distributions with the +/// specified Name. +MemorySizeDistribution +getDistributionOrDie(ArrayRef Distributions, + StringRef Name); + } // namespace libc_benchmarks } // namespace llvm