From 0f8b6bfc991b6a9c0d5671e107ec237849e865dc Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Wed, 15 Jul 2026 16:40:19 +0200 Subject: [PATCH] feat: Add benchmarks for source code scanning --- .clang-format | 2 +- CMakeLists.txt | 8 ++ benchmark/CMakeLists.txt | 47 +++++++++++ benchmark/benchmark_hashutil.cpp | 139 +++++++++++++++++++++++++++++++ benchmark/main.cpp | 21 +++++ doc/install.md | 2 + 6 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 benchmark/CMakeLists.txt create mode 100644 benchmark/benchmark_hashutil.cpp create mode 100644 benchmark/main.cpp diff --git a/.clang-format b/.clang-format index 48a15421..87bf8073 100644 --- a/.clang-format +++ b/.clang-format @@ -32,7 +32,7 @@ IncludeCategories: - Regex: '^$' + - Regex: '^<(benchmark/.*|blake3\.h|cxxurl/url\.hpp|doctest/.*|fmt/.*|hiredis/.*|httplib\.h|tl/expected\.hpp|xx(hash|h_x86dispatch)\.h|zstd\.h)>$' Priority: 3 # System headers: - Regex: '\.h.*>$' diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c2657f2..c65b0018 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ include(EnableCcache) option(REDIS_STORAGE_BACKEND "Enable Redis remote storage" ON) option(HTTP_STORAGE_BACKEND "Enable HTTP remote storage" ON) +option(ENABLE_BENCHMARKS "Enable benchmarks" OFF) option(ENABLE_TESTING "Enable tests" ON) include(InstallDirs) @@ -151,6 +152,13 @@ if(ENABLE_TESTING) DEPENDS ccache unittest) endif() +# +# Benchmarks +# +if(ENABLE_BENCHMARKS) + add_subdirectory(benchmark) +endif() + # # Special formatting targets # diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt new file mode 100644 index 00000000..dc99847f --- /dev/null +++ b/benchmark/CMakeLists.txt @@ -0,0 +1,47 @@ +include(FetchContent) + +set(_benchmark_version_string 1.9.5) +message(STATUS "Downloading Google Benchmark ${_benchmark_version_string}") + +set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE) +set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) +set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) +set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE) +set(BENCHMARK_DOWNLOAD_DEPENDENCIES OFF CACHE BOOL "" FORCE) + +FetchContent_Declare( + benchmark + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + URL "https://github.com/google/benchmark/archive/refs/tags/v${_benchmark_version_string}.tar.gz" + URL_HASH SHA256=9631341c82bac4a288bef951f8b26b41f69021794184ece969f8473977eaa340 +) +FetchContent_MakeAvailable(benchmark) + +register_dependency(GoogleBenchmark DOWNLOADED "${_benchmark_version_string}") + +set( + source_files + main.cpp + benchmark_hashutil.cpp +) + +add_executable(ccache_benchmark ${source_files}) + +target_link_libraries( + ccache_benchmark + PRIVATE standard_settings standard_warnings ccache_framework benchmark::benchmark +) + +target_include_directories( + ccache_benchmark + PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${ccache_SOURCE_DIR}/src +) + +add_custom_target( + benchmarks + COMMAND ccache_benchmark + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS ccache_benchmark + COMMENT "Running benchmarks" + USES_TERMINAL +) diff --git a/benchmark/benchmark_hashutil.cpp b/benchmark/benchmark_hashutil.cpp new file mode 100644 index 00000000..244424fd --- /dev/null +++ b/benchmark/benchmark_hashutil.cpp @@ -0,0 +1,139 @@ +// Copyright (C) 2026 Joel Rosdahl and other contributors +// +// See doc/authors.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include +#include + +#include + +#include +#include + +namespace { + +std::string +generate_source_code(size_t size) +{ + // Synthetic preprocessed-C++-like code, hopefully reasonably representative + // of real source code. + // + // For our purposes, it's the frequency of _ (for temporal macro matching), . + // (for incbin matching) and # (for embed matching) that is of most + // importance. + static constexpr std::string_view corpus = + R"(# 42 "/usr/include/foo/bar/banana.h" 3 + +namespace std _FOO_VISIBILITY(default) { + +constexpr double __one = 17.0 / 42.0; +auto __pair = std::make_pair(__one, __one); +__result._some_payload._do_bar(__one); +__state._some_storage._do_foo(); + +const char* __msg = "hello world"; +const char* __file = "/usr/include/hello.h"; + +template +inline _ForwardIterator +__some_name(_ForwardIterator __first, _ForwardIterator __last, + _ForwardIterator __middle, _Compare __comp) +{ + while (true) { + while (__comp(__first, __middle)) + ++__first; + --__last; + while (__comp(__middle, __last)) + --__last; + if (!(__first < __last)) + return __first; + std::iter_swap(__first, __last); + ++__first; + } +} + +// ... cut along line #eh4711 ... + +template +inline void +__do_sort(_MyAwesomeIterator __first, _MyAwesomeIterator __last) +{ + for (_MyAwesomeIterator __i = __first + 1; __i != __last; ++__i) + if (__some::__ops::__do_foo()(__i, __first)) + std::something(__first, __i, __i + 1); +} + +template +inline _Tp +__what_what(_Tp __one, _Tp __two, _Tp __three) +{ + return foo(bar(__one, __two), __three); +} + +} +)"; + + std::string source; + source.reserve(size); + + while (source.size() < size) { + source += corpus; + } + + source.resize(size); + return source; +} + +} // namespace + +#ifdef HAVE_AVX2 +static void +BM_check_for_source_code_patterns_avx2(benchmark::State& state) +{ + const auto source = generate_source_code(state.range(0)); + for (auto _ : state) { + auto result = check_for_source_code_patterns_avx2(source); + benchmark::DoNotOptimize(result); + } + state.SetBytesProcessed(state.iterations() * source.size()); +} +#endif + +static void +BM_check_for_source_code_patterns_scalar(benchmark::State& state) +{ + const auto source = generate_source_code(state.range(0)); + for (auto _ : state) { + auto result = check_for_source_code_patterns_scalar(source); + benchmark::DoNotOptimize(result); + } + state.SetBytesProcessed(state.iterations() * source.size()); +} + +#ifdef HAVE_AVX2 +BENCHMARK(BM_check_for_source_code_patterns_avx2) + ->Arg(1000) + ->Arg(10000) + ->Arg(100000) + ->Arg(1000000); +#endif + +BENCHMARK(BM_check_for_source_code_patterns_scalar) + ->Arg(1000) + ->Arg(10000) + ->Arg(100000) + ->Arg(1000000); diff --git a/benchmark/main.cpp b/benchmark/main.cpp new file mode 100644 index 00000000..335ea889 --- /dev/null +++ b/benchmark/main.cpp @@ -0,0 +1,21 @@ +// Copyright (C) 2026 Joel Rosdahl and other contributors +// +// See doc/authors.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include + +BENCHMARK_MAIN(); diff --git a/doc/install.md b/doc/install.md index c190cdcf..9473190e 100644 --- a/doc/install.md +++ b/doc/install.md @@ -54,6 +54,8 @@ should be located or retrieved: HTTP_STORAGE_BACKEND=OFF`) - [doctest](https://github.com/doctest/doctest)[^2] (disable with `-D ENABLE_TESTING=OFF`) +- [Google Benchmark](https://github.com/google/benchmark)[^2] (enable with `-D + ENABLE_BENCHMARKS=ON`) - [hiredis](https://github.com/redis/hiredis)[^2] (disable with `-D REDIS_STORAGE_BACKEND=OFF`) -- 2.47.3