[libc-commits] [libc] [libc][benchmark] allow benchmark to be built without llvm support (PR #200951)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Mon Jun 1 14:46:31 PDT 2026
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/200951
>From 4758f8b095a88ba6dd469f5deb52d13f8a8e3839 Mon Sep 17 00:00:00 2001
From: Yifan Zhu <yfzhu at google.com>
Date: Mon, 1 Jun 2026 14:42:33 -0700
Subject: [PATCH] [libc][benchmark] allow benchmark to be built without llvm
support
---
libc/benchmarks/CMakeLists.txt | 148 ++++++++++++------
libc/benchmarks/LibcBenchmark.cpp | 27 ++++
libc/benchmarks/LibcBenchmark.h | 11 +-
libc/benchmarks/LibcMemoryBenchmark.cpp | 27 +++-
libc/benchmarks/LibcMemoryBenchmark.h | 17 +-
.../LibcMemoryGoogleBenchmarkMain.cpp | 8 +
libc/benchmarks/MemorySizeDistributions.cpp | 14 ++
7 files changed, 192 insertions(+), 60 deletions(-)
diff --git a/libc/benchmarks/CMakeLists.txt b/libc/benchmarks/CMakeLists.txt
index 65c7cd76fad29..ebb270c7c2dc8 100644
--- a/libc/benchmarks/CMakeLists.txt
+++ b/libc/benchmarks/CMakeLists.txt
@@ -10,16 +10,28 @@ endif()
find_package(Threads)
-set(LLVM_LINK_COMPONENTS
- Support
- TargetParser
- )
+option(LIBC_BENCHMARKS_HAS_LLVM_SUPPORT "Avoid LLVM Support dependency and reporting CPU details" ON)
+
+if(NOT TARGET LLVMSupport OR NOT TARGET LLVMTargetParser)
+ set(LIBC_BENCHMARKS_HAS_LLVM_SUPPORT OFF CACHE BOOL "Avoid LLVM Support dependency and reporting CPU details" FORCE)
+endif()
+
+if(LIBC_BENCHMARKS_HAS_LLVM_SUPPORT)
+ set(LLVM_LINK_COMPONENTS
+ Support
+ TargetParser
+ )
+endif()
#==============================================================================
# Add Unit Testing Support
#==============================================================================
-make_gtest_target()
+if(COMMAND build_gtest)
+ build_gtest()
+elseif(COMMAND make_gtest_target)
+ make_gtest_target()
+endif()
function(add_libc_benchmark_unittest target_name)
if(NOT LLVM_INCLUDE_TESTS)
@@ -64,6 +76,9 @@ ExternalProject_Add(google-benchmark-libc
PREFIX google-benchmark-libc
SOURCE_DIR ${LLVM_THIRD_PARTY_DIR}/benchmark
INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc
+ BUILD_BYPRODUCTS
+ ${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc/lib/libbenchmark.a
+ ${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc/lib/libbenchmark_main.a
CMAKE_CACHE_ARGS
-DBENCHMARK_ENABLE_EXCEPTIONS:BOOL=OFF
-DBENCHMARK_ENABLE_LTO:BOOL=OFF
@@ -87,6 +102,26 @@ ExternalProject_Add(google-benchmark-libc
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc/include")
+
+if(NOT TARGET benchmark::benchmark)
+ add_library(benchmark::benchmark STATIC IMPORTED)
+ set_target_properties(benchmark::benchmark PROPERTIES
+ IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc/lib/libbenchmark.a"
+ INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc/include"
+ )
+ add_dependencies(benchmark::benchmark google-benchmark-libc)
+endif()
+
+if(NOT TARGET benchmark_main)
+ add_library(benchmark_main STATIC IMPORTED)
+ set_target_properties(benchmark_main PROPERTIES
+ IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc/lib/libbenchmark_main.a"
+ INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/google-benchmark-libc/include"
+ )
+ add_dependencies(benchmark_main google-benchmark-libc)
+endif()
+
add_custom_target(libc-benchmark-util-tests)
# libc-benchmark
@@ -103,10 +138,18 @@ target_include_directories(libc-benchmark
target_link_libraries(libc-benchmark
PUBLIC
benchmark::benchmark
- LLVMSupport
- LLVMTargetParser
Threads::Threads
)
+if(LIBC_BENCHMARKS_HAS_LLVM_SUPPORT)
+ target_link_libraries(libc-benchmark
+ PUBLIC
+ LLVMSupport
+ LLVMTargetParser
+ )
+ target_compile_definitions(libc-benchmark PUBLIC "LIBC_BENCHMARKS_HAS_LLVM_SUPPORT")
+else()
+ target_compile_definitions(libc-benchmark PUBLIC "LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING=1")
+endif()
add_dependencies(libc-benchmark google-benchmark-libc)
llvm_update_compile_flags(libc-benchmark)
@@ -141,54 +184,56 @@ add_libc_benchmark_unittest(libc-memory-benchmark-test
DEPENDS libc-memory-benchmark
)
-# json
-add_library(json
- STATIC
- EXCLUDE_FROM_ALL
- JSON.cpp
- JSON.h
-)
-target_link_libraries(json PUBLIC libc-memory-benchmark)
-llvm_update_compile_flags(json)
+if(LIBC_BENCHMARKS_HAS_LLVM_SUPPORT)
+ # json
+ add_library(json
+ STATIC
+ EXCLUDE_FROM_ALL
+ JSON.cpp
+ JSON.h
+ )
+ target_link_libraries(json PUBLIC libc-memory-benchmark)
+ llvm_update_compile_flags(json)
-add_libc_benchmark_unittest(json-test
- SRCS JSONTest.cpp
- DEPENDS json
-)
+ add_libc_benchmark_unittest(json-test
+ SRCS JSONTest.cpp
+ DEPENDS json
+ )
-#==============================================================================
-# Benchmarking tool
-#==============================================================================
+ #==============================================================================
+ # Benchmarking tool
+ #==============================================================================
-# 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}=LIBC_NAMESPACE::${name}" "-DLIBC_BENCHMARK_FUNCTION_NAME=\"${fq_config_name}\"")
- llvm_update_compile_flags(${benchmark_name})
- else()
- message(STATUS "Skipping benchmark for '${fq_config_name}' insufficient host cpu features '${required_cpu_features}'")
- endif()
- endforeach()
-endfunction()
+ # 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}=LIBC_NAMESPACE::${name}" "-DLIBC_BENCHMARK_FUNCTION_NAME=\"${fq_config_name}\"")
+ llvm_update_compile_flags(${benchmark_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(bcmp)
-add_libc_multi_impl_benchmark(bzero)
-add_libc_multi_impl_benchmark(memcmp)
-add_libc_multi_impl_benchmark(memcpy)
-add_libc_multi_impl_benchmark(memmove)
-add_libc_multi_impl_benchmark(memset)
+ add_libc_multi_impl_benchmark(bcmp)
+ add_libc_multi_impl_benchmark(bzero)
+ add_libc_multi_impl_benchmark(memcmp)
+ add_libc_multi_impl_benchmark(memcpy)
+ add_libc_multi_impl_benchmark(memmove)
+ add_libc_multi_impl_benchmark(memset)
+endif()
#==============================================================================
# Google Benchmarking tool
@@ -214,3 +259,4 @@ target_link_libraries(libc.benchmarks.memory_functions.opt_host
benchmark_main
)
llvm_update_compile_flags(libc.benchmarks.memory_functions.opt_host)
+add_dependencies(libc.benchmarks.memory_functions.opt_host google-benchmark-libc)
diff --git a/libc/benchmarks/LibcBenchmark.cpp b/libc/benchmarks/LibcBenchmark.cpp
index 014b855fe987f..a5bca1f71ebea 100644
--- a/libc/benchmarks/LibcBenchmark.cpp
+++ b/libc/benchmarks/LibcBenchmark.cpp
@@ -8,7 +8,9 @@
#include "LibcBenchmark.h"
#include "llvm/ADT/StringRef.h"
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
#include "llvm/TargetParser/Host.h"
+#endif
namespace llvm {
namespace libc_benchmarks {
@@ -25,7 +27,11 @@ HostState HostState::get() {
const auto &CpuInfo = benchmark::CPUInfo::Get();
HostState H;
H.CpuFrequency = CpuInfo.cycles_per_second;
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
H.CpuName = llvm::sys::getHostCPUName().str();
+#else
+ H.CpuName = "";
+#endif
for (const auto &BenchmarkCacheInfo : CpuInfo.caches) {
CacheInfo CI;
CI.Type = BenchmarkCacheInfo.type;
@@ -39,3 +45,24 @@ HostState HostState::get() {
} // namespace libc_benchmarks
} // namespace llvm
+
+#ifndef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
+#include "llvm/ADT/Twine.h"
+#include <cstdlib>
+#include <iostream>
+
+namespace llvm {
+void report_fatal_error(const char *reason, bool gen_crash_diag) {
+ std::cerr << "Fatal error: " << reason << std::endl;
+ std::abort();
+}
+void report_fatal_error(StringRef reason, bool gen_crash_diag) {
+ std::cerr << "Fatal error: " << reason.str() << std::endl;
+ std::abort();
+}
+void report_fatal_error(const Twine &reason, bool gen_crash_diag) {
+ std::cerr << "Fatal error: (twine reason)" << std::endl;
+ std::abort();
+}
+} // namespace llvm
+#endif
diff --git a/libc/benchmarks/LibcBenchmark.h b/libc/benchmarks/LibcBenchmark.h
index 6b1556721e416..a790c3ad43d6d 100644
--- a/libc/benchmarks/LibcBenchmark.h
+++ b/libc/benchmarks/LibcBenchmark.h
@@ -39,6 +39,8 @@
#include <cstdint>
#include <optional>
+#include "llvm/Support/ErrorHandling.h"
+
namespace llvm {
namespace libc_benchmarks {
@@ -95,11 +97,18 @@ struct BenchmarkState {
// current samples.
};
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
+using BenchmarkLogType = llvm::SmallVector<BenchmarkState, 16>;
+#else
+#include <vector>
+using BenchmarkLogType = std::vector<BenchmarkState>;
+#endif
+
// A lightweight result for a benchmark.
struct BenchmarkResult {
BenchmarkStatus TerminationStatus = BenchmarkStatus::Running;
Duration BestGuess = {};
- std::optional<llvm::SmallVector<BenchmarkState, 16>> MaybeBenchmarkLog;
+ std::optional<BenchmarkLogType> MaybeBenchmarkLog;
};
// Stores information about a cache in the host memory system.
diff --git a/libc/benchmarks/LibcMemoryBenchmark.cpp b/libc/benchmarks/LibcMemoryBenchmark.cpp
index 9307ee45b2853..a0500ff0201df 100644
--- a/libc/benchmarks/LibcMemoryBenchmark.cpp
+++ b/libc/benchmarks/LibcMemoryBenchmark.cpp
@@ -8,10 +8,13 @@
#include "LibcMemoryBenchmark.h"
#include "llvm/ADT/SmallVector.h"
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
+#endif
#include "llvm/Support/MathExtras.h"
#include <algorithm>
+#include <optional>
namespace llvm {
namespace libc_benchmarks {
@@ -61,7 +64,8 @@ MismatchOffsetDistribution::MismatchOffsetDistribution(size_t BufferSize,
std::uniform_int_distribution<size_t>(0, MismatchIndices.size() - 1);
}
-static size_t getL1DataCacheSize() {
+static std::optional<size_t> getL1DataCacheSize() {
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
const std::vector<CacheInfo> &CacheInfos = HostState::get().Caches;
const auto IsL1DataCache = [](const CacheInfo &CI) {
return CI.Type == "Data" && CI.Level == 1;
@@ -70,6 +74,9 @@ static size_t getL1DataCacheSize() {
if (CacheIt != CacheInfos.end())
return CacheIt->Size;
report_fatal_error("Unable to read L1 Cache Data Size");
+#else
+ return std::nullopt;
+#endif
}
static constexpr int64_t KiB = 1024;
@@ -77,7 +84,9 @@ static constexpr int64_t ParameterStorageBytes = 4 * KiB;
static constexpr int64_t L1LeftAsideBytes = 1 * KiB;
static size_t getAvailableBufferSize() {
- return getL1DataCacheSize() - L1LeftAsideBytes - ParameterStorageBytes;
+ auto L1Size = getL1DataCacheSize();
+ return (L1Size ? *L1Size : 32 * 1024) - L1LeftAsideBytes -
+ ParameterStorageBytes;
}
ParameterBatch::ParameterBatch(size_t BufferCount)
@@ -88,7 +97,8 @@ ParameterBatch::ParameterBatch(size_t BufferCount)
report_fatal_error("Not enough L1 cache");
const size_t ParameterBytes = Parameters.size() * sizeof(ParameterType);
const size_t BufferBytes = BufferSize * BufferCount;
- if (ParameterBytes + BufferBytes + L1LeftAsideBytes > getL1DataCacheSize())
+ auto L1Size = getL1DataCacheSize();
+ if (L1Size && ParameterBytes + BufferBytes + L1LeftAsideBytes > *L1Size)
report_fatal_error(
"We're splitting a buffer of the size of the L1 cache between a data "
"buffer and a benchmark parameters buffer, so by construction the "
@@ -103,7 +113,8 @@ size_t ParameterBatch::getBatchBytes() const {
}
void ParameterBatch::checkValid(const ParameterType &P) const {
- if (P.OffsetBytes + P.SizeBytes >= BufferSize)
+ if (P.OffsetBytes + P.SizeBytes >= BufferSize) {
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
report_fatal_error(
llvm::Twine("Call would result in buffer overflow: Offset=")
.concat(llvm::Twine(P.OffsetBytes))
@@ -111,6 +122,14 @@ void ParameterBatch::checkValid(const ParameterType &P) const {
.concat(llvm::Twine(P.SizeBytes))
.concat(", BufferSize=")
.concat(llvm::Twine(BufferSize)));
+#else
+ std::string Message = "Call would result in buffer overflow: Offset=" +
+ std::to_string(P.OffsetBytes) +
+ ", Size=" + std::to_string(P.SizeBytes) +
+ ", BufferSize=" + std::to_string(BufferSize);
+ report_fatal_error(Message.c_str());
+#endif
+ }
}
CopySetup::CopySetup()
diff --git a/libc/benchmarks/LibcMemoryBenchmark.h b/libc/benchmarks/LibcMemoryBenchmark.h
index 5ba8b936a0caf..eca50d618ad64 100644
--- a/libc/benchmarks/LibcMemoryBenchmark.h
+++ b/libc/benchmarks/LibcMemoryBenchmark.h
@@ -21,6 +21,7 @@
#include <cstdint>
#include <optional>
#include <random>
+#include <vector>
namespace llvm {
namespace libc_benchmarks {
@@ -83,7 +84,7 @@ struct Runtime {
// The benchmark options that were used to perform the measurement.
// This is decided by the framework.
- BenchmarkOptions BenchmarkOptions;
+ struct BenchmarkOptions BenchmarkOptions;
};
//--------
@@ -93,7 +94,7 @@ struct Runtime {
// The root object containing all the data (configuration and measurements).
struct Study {
std::string StudyName;
- Runtime Runtime;
+ struct Runtime Runtime;
StudyConfiguration Configuration;
std::vector<Duration> Measurements;
};
@@ -139,12 +140,20 @@ class OffsetDistribution {
}
};
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
+using MismatchIndicesType = llvm::SmallVector<uint32_t, 16>;
+using MismatchIndicesImplType = llvm::SmallVectorImpl<uint32_t>;
+#else
+using MismatchIndicesType = std::vector<uint32_t>;
+using MismatchIndicesImplType = std::vector<uint32_t>;
+#endif
+
// Helper to generate random buffer offsets that satisfy the configuration
// constraints. It is specifically designed to benchmark `memcmp` functions
// where we may want the Nth byte to differ.
class MismatchOffsetDistribution {
std::uniform_int_distribution<size_t> MismatchIndexSelector;
- llvm::SmallVector<uint32_t, 16> MismatchIndices;
+ MismatchIndicesType MismatchIndices;
const uint32_t MismatchAt;
public:
@@ -153,7 +162,7 @@ class MismatchOffsetDistribution {
explicit operator bool() const { return !MismatchIndices.empty(); }
- const llvm::SmallVectorImpl<uint32_t> &getMismatchIndices() const {
+ const MismatchIndicesImplType &getMismatchIndices() const {
return MismatchIndices;
}
diff --git a/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp b/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp
index 164708ad1a05a..db75a6e7453bc 100644
--- a/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp
+++ b/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp
@@ -3,7 +3,9 @@
#include "MemorySizeDistributions.h"
#include "benchmark/benchmark.h"
#include "llvm/ADT/ArrayRef.h"
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
#include "llvm/ADT/Twine.h"
+#endif
#include <chrono>
#include <cstdint>
#include <random>
@@ -11,7 +13,9 @@
using llvm::Align;
using llvm::ArrayRef;
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
using llvm::Twine;
+#endif
using llvm::libc_benchmarks::BzeroConfiguration;
using llvm::libc_benchmarks::ComparisonSetup;
using llvm::libc_benchmarks::CopySetup;
@@ -53,7 +57,11 @@ template <typename SetupType, typename ConfigurationType> struct Runner {
(State.iterations() * Setup.getBatchBytes()) / Setup.BatchSize;
State.SetBytesProcessed(TotalBytes);
State.SetItemsProcessed(State.iterations());
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
State.SetLabel((Twine(Configuration.Name) + "," + Distribution.Name).str());
+#else
+ State.SetLabel(Configuration.Name.str() + "," + Distribution.Name.str());
+#endif
State.counters["bytes_per_cycle"] = benchmark::Counter(
TotalBytes / benchmark::CPUInfo::Get().cycles_per_second,
benchmark::Counter::kIsRate);
diff --git a/libc/benchmarks/MemorySizeDistributions.cpp b/libc/benchmarks/MemorySizeDistributions.cpp
index e29b3710f7367..9e7469cc81a50 100644
--- a/libc/benchmarks/MemorySizeDistributions.cpp
+++ b/libc/benchmarks/MemorySizeDistributions.cpp
@@ -1,7 +1,12 @@
#include "MemorySizeDistributions.h"
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#else
+#include <iostream>
+#include <sstream>
+#endif
namespace llvm {
namespace libc_benchmarks {
@@ -189,6 +194,7 @@ getDistributionOrDie(ArrayRef<MemorySizeDistribution> Distributions,
if (MSD.Name == Name)
return MSD;
+#ifdef LIBC_BENCHMARKS_HAS_LLVM_SUPPORT
std::string Message;
raw_string_ostream Stream(Message);
Stream << "Unknown MemorySizeDistribution '" << Name
@@ -196,6 +202,14 @@ getDistributionOrDie(ArrayRef<MemorySizeDistribution> Distributions,
for (const auto &MSD : Distributions)
Stream << "'" << MSD.Name << "'\n";
report_fatal_error(Message);
+#else
+ std::stringstream Stream;
+ Stream << "Unknown MemorySizeDistribution '" << std::string(Name)
+ << "', available distributions:\n";
+ for (const auto &MSD : Distributions)
+ Stream << "'" << MSD.Name.str() << "'\n";
+ report_fatal_error(Stream.str());
+#endif
}
} // namespace libc_benchmarks
More information about the libc-commits
mailing list