[libc] Generate one benchmark per implementation

We now generate as many benchmarks as there are implementations.

Differential Revision: https://reviews.llvm.org/D102156
This commit is contained in:
Guillaume Chatelet 2021-06-10 13:04:56 +00:00
parent 0bd5bbb31e
commit 8d64ed8544
2 changed files with 40 additions and 48 deletions

View File

@ -145,11 +145,27 @@ add_libc_benchmark_unittest(json-test
# Benchmarking tool
#==============================================================================
add_executable(libc-benchmark-main
EXCLUDE_FROM_ALL
LibcMemoryBenchmarkMain.cpp
)
foreach(entrypoint_target libc.src.string.memcpy libc.src.string.memset)
get_target_property(entrypoint_object_file ${entrypoint_target} "OBJECT_FILE_RAW")
target_link_libraries(libc-benchmark-main PUBLIC json ${entrypoint_object_file})
endforeach()
# Benchmark all implementations that can run on the target CPU.
function(add_libc_multi_impl_benchmark name)
get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations)
foreach(fq_config_name IN LISTS fq_implementations)
get_target_property(required_cpu_features ${fq_config_name} REQUIRE_CPU_FEATURES)
cpu_supports(can_run "${required_cpu_features}")
if(can_run)
set(benchmark_name ${fq_config_name}_benchmark)
add_executable(${benchmark_name}
EXCLUDE_FROM_ALL
LibcMemoryBenchmarkMain.cpp
)
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}\"")
else()
message(STATUS "Skipping benchmark for '${fq_config_name}' insufficient host cpu features '${required_cpu_features}'")
endif()
endforeach()
endfunction()
add_libc_multi_impl_benchmark(memcpy)
add_libc_multi_impl_benchmark(memset)

View File

@ -27,17 +27,9 @@ extern void *memset(void *, int, size_t);
namespace llvm {
namespace libc_benchmarks {
enum Function { memcpy, memset };
static cl::opt<std::string>
StudyName("study-name", cl::desc("The name for this study"), cl::Required);
static cl::opt<Function>
MemoryFunction("function", cl::desc("Sets the function to benchmark:"),
cl::values(clEnumVal(memcpy, "__llvm_libc::memcpy"),
clEnumVal(memset, "__llvm_libc::memset")),
cl::Required);
static cl::opt<std::string>
SizeDistributionName("size-distribution-name",
cl::desc("The name of the distribution to use"));
@ -75,12 +67,12 @@ struct ParameterType {
unsigned SizeBytes : 16; // max : 16 KiB - 1
};
struct MemcpyBenchmark {
#if defined(LIBC_BENCHMARK_FUNCTION_MEMCPY)
struct Benchmark {
static constexpr auto GetDistributions = &getMemcpySizeDistributions;
static constexpr size_t BufferCount = 2;
static void amend(Study &S) { S.Configuration.Function = "memcpy"; }
MemcpyBenchmark(const size_t BufferSize)
Benchmark(const size_t BufferSize)
: SrcBuffer(BufferSize), DstBuffer(BufferSize) {}
inline auto functor() {
@ -94,13 +86,12 @@ struct MemcpyBenchmark {
AlignedBuffer SrcBuffer;
AlignedBuffer DstBuffer;
};
struct MemsetBenchmark {
#elif defined(LIBC_BENCHMARK_FUNCTION_MEMSET)
struct Benchmark {
static constexpr auto GetDistributions = &getMemsetSizeDistributions;
static constexpr size_t BufferCount = 1;
static void amend(Study &S) { S.Configuration.Function = "memset"; }
MemsetBenchmark(const size_t BufferSize) : DstBuffer(BufferSize) {}
Benchmark(const size_t BufferSize) : DstBuffer(BufferSize) {}
inline auto functor() {
return [this](ParameterType P) {
@ -112,9 +103,11 @@ struct MemsetBenchmark {
AlignedBuffer DstBuffer;
};
#else
#error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition"
#endif
template <typename Benchmark> struct Harness : Benchmark {
using Benchmark::functor;
struct Harness : Benchmark {
Harness(const size_t BufferSize, size_t BatchParameterCount,
std::function<unsigned()> SizeSampler,
@ -140,11 +133,6 @@ private:
std::function<unsigned()> OffsetSampler;
};
struct IBenchmark {
virtual ~IBenchmark() {}
virtual Study run() = 0;
};
size_t getL1DataCacheSize() {
const std::vector<CacheInfo> &CacheInfos = HostState::get().Caches;
const auto IsL1DataCache = [](const CacheInfo &CI) {
@ -156,7 +144,7 @@ size_t getL1DataCacheSize() {
report_fatal_error("Unable to read L1 Cache Data Size");
}
template <typename Benchmark> struct MemfunctionBenchmark : IBenchmark {
struct MemfunctionBenchmark {
MemfunctionBenchmark(int64_t L1Size = getL1DataCacheSize())
: AvailableSize(L1Size - L1LeftAsideBytes - ParameterStorageBytes),
BufferSize(AvailableSize / Benchmark::BufferCount),
@ -217,12 +205,10 @@ template <typename Benchmark> struct MemfunctionBenchmark : IBenchmark {
else
SC.SizeDistributionName = SizeDistributionName;
SC.AccessAlignment = MaybeAlign(AlignedAccess);
// Delegate specific flags and configuration.
Benchmark::amend(Study);
SC.Function = LIBC_BENCHMARK_FUNCTION_NAME;
}
Study run() override {
Study run() {
if (SweepMode)
runSweepMode();
else
@ -280,8 +266,7 @@ private:
void runTrials(const BenchmarkOptions &Options,
std::function<unsigned()> SizeSampler,
std::function<unsigned()> OffsetSampler) {
Harness<Benchmark> B(BufferSize, BatchParameterCount, SizeSampler,
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);
@ -313,15 +298,6 @@ private:
}
};
std::unique_ptr<IBenchmark> getMemfunctionBenchmark() {
switch (MemoryFunction) {
case memcpy:
return std::make_unique<MemfunctionBenchmark<MemcpyBenchmark>>();
case memset:
return std::make_unique<MemfunctionBenchmark<MemsetBenchmark>>();
}
}
void writeStudy(const Study &S) {
std::error_code EC;
raw_fd_ostream FOS(Output, EC);
@ -337,8 +313,8 @@ void writeStudy(const Study &S) {
void main() {
checkRequirements();
auto MB = getMemfunctionBenchmark();
writeStudy(MB->run());
MemfunctionBenchmark MB;
writeStudy(MB.run());
}
} // namespace libc_benchmarks