From 7fe2c69d4293ebaf4e4284735dd37f525ec0f2aa Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Tue, 16 Jun 2026 21:40:49 +0100 Subject: [PATCH 1/3] Initial work on modules --- .github/workflows/ci.yml | 115 ------------------ .github/workflows/clang.yml | 34 ++++++ .github/workflows/gcc.yml | 33 +++++ .github/workflows/macos.yml | 31 +++++ .github/workflows/msvc.yml | 29 +++++ .gitignore | 1 + CMakeLists.txt | 17 ++- cmake/install-config.cmake | 4 +- cmake/install-rules.cmake | 50 ++++---- cmake/modules-files.cmake | 26 ++++ include/bencher/event_counter.hpp | 30 ----- include/bencher/file.hpp | 18 --- .../bencher/bar_chart.ixx | 28 ++--- .../bencher/bencher.ixx | 60 ++++----- .../bencher/cache_clearer.ixx | 12 +- {include => modules}/bencher/config.hpp | 2 - .../counters/apple_arm_perf_events.ixx | 36 +++--- .../bencher/counters/linux_perf_events.ixx | 37 +++--- .../bencher/counters/windows_perf_events.ixx | 23 ++-- .../bencher/diagnostics.ixx | 17 +-- .../bencher/do_not_optimize.ixx | 17 +-- modules/bencher/event_counter.ixx | 35 ++++++ modules/bencher/file.ixx | 17 +++ .../json.hpp => modules/bencher/json.ixx | 18 +-- .../bencher/line_chart.ixx | 12 +- .../bencher/radar_chart.ixx | 15 +-- src/main.cpp | 8 +- tests/CMakeLists.txt | 5 +- tests/unit_test/unit_test.cpp | 19 +-- 29 files changed, 399 insertions(+), 350 deletions(-) delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/clang.yml create mode 100644 .github/workflows/gcc.yml create mode 100644 .github/workflows/macos.yml create mode 100644 .github/workflows/msvc.yml create mode 100644 cmake/modules-files.cmake delete mode 100644 include/bencher/event_counter.hpp delete mode 100644 include/bencher/file.hpp rename include/bencher/bar_chart.hpp => modules/bencher/bar_chart.ixx (95%) rename include/bencher/bencher.hpp => modules/bencher/bencher.ixx (95%) rename include/bencher/cache_clearer.hpp => modules/bencher/cache_clearer.ixx (97%) rename {include => modules}/bencher/config.hpp (98%) rename include/bencher/counters/apple_arm_perf_events.hpp => modules/bencher/counters/apple_arm_perf_events.ixx (97%) rename include/bencher/counters/linux_perf_events.hpp => modules/bencher/counters/linux_perf_events.ixx (91%) rename include/bencher/counters/windows_perf_events.hpp => modules/bencher/counters/windows_perf_events.ixx (78%) rename include/bencher/diagnostics.hpp => modules/bencher/diagnostics.ixx (96%) rename include/bencher/do_not_optimize.hpp => modules/bencher/do_not_optimize.ixx (84%) create mode 100644 modules/bencher/event_counter.ixx create mode 100644 modules/bencher/file.ixx rename include/bencher/json.hpp => modules/bencher/json.ixx (60%) rename include/bencher/line_chart.hpp => modules/bencher/line_chart.ixx (98%) rename include/bencher/radar_chart.hpp => modules/bencher/radar_chart.ixx (99%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 4b5508b..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: CI - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - gcc: - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - gcc_version: [13, 14] - - steps: - - uses: actions/checkout@v4 - - - name: Install GCC - run: | - sudo apt-get update - sudo apt-get install -y gcc-${{ matrix.gcc_version }} g++-${{ matrix.gcc_version }} - - - name: Configure CMake - run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DCMAKE_CXX_COMPILER=g++-${{ matrix.gcc_version }} \ - -DBUILD_TESTING=ON - - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $(nproc) - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure - - clang: - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - clang_version: [17, 18, 19] - - steps: - - uses: actions/checkout@v4 - - - name: Install Clang - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh ${{ matrix.clang_version }} - sudo apt-get install -y libc++-${{ matrix.clang_version }}-dev libc++abi-${{ matrix.clang_version }}-dev - - - name: Configure CMake - run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DCMAKE_CXX_COMPILER=clang++-${{ matrix.clang_version }} \ - -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ - -DBUILD_TESTING=ON - - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $(nproc) - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure - - msvc: - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - - steps: - - uses: actions/checkout@v4 - - - name: Configure CMake - run: | - cmake -B build -DBUILD_TESTING=ON - - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $env:NUMBER_OF_PROCESSORS - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure - - macos: - runs-on: macos-latest - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - - steps: - - uses: actions/checkout@v4 - - - name: Configure CMake - run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DBUILD_TESTING=ON - - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $(sysctl -n hw.ncpu) - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure diff --git a/.github/workflows/clang.yml b/.github/workflows/clang.yml new file mode 100644 index 0000000..195e787 --- /dev/null +++ b/.github/workflows/clang.yml @@ -0,0 +1,34 @@ +name: Clang + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + clang: + runs-on: ubuntu-26.04 + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + clang_version: [21, 22] + + steps: + - uses: actions/checkout@v6 + + - name: Configure CMake + run: | + cmake -B build \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DCMAKE_CXX_COMPILER=clang++-${{ matrix.clang_version }} \ + -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ + -DBUILD_TESTING=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} -j $(nproc) + + - name: Test + working-directory: build + run: ctest -C ${{ matrix.build_type }} --output-on-failure diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml new file mode 100644 index 0000000..be73d2f --- /dev/null +++ b/.github/workflows/gcc.yml @@ -0,0 +1,33 @@ +name: GCC + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + gcc: + runs-on: ubuntu-26.04 + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + gcc_version: [15] + + steps: + - uses: actions/checkout@v6 + + - name: Configure CMake + run: | + cmake -B build \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DCMAKE_CXX_COMPILER=g++-${{ matrix.gcc_version }} \ + -DBUILD_TESTING=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} -j $(nproc) + + - name: Test + working-directory: build + run: ctest -C ${{ matrix.build_type }} --output-on-failure diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..7694037 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,31 @@ +name: macOS + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + macos: + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + + steps: + - uses: actions/checkout@v6 + + - name: Configure CMake + run: | + cmake -B build \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DBUILD_TESTING=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} -j $(sysctl -n hw.ncpu) + + - name: Test + working-directory: build + run: ctest -C ${{ matrix.build_type }} --output-on-failure diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml new file mode 100644 index 0000000..720bd49 --- /dev/null +++ b/.github/workflows/msvc.yml @@ -0,0 +1,29 @@ +name: MSVC + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + msvc: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + + steps: + - uses: actions/checkout@v6 + + - name: Configure CMake + run: | + cmake -B build -DBUILD_TESTING=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} -j $env:NUMBER_OF_PROCESSORS + + - name: Test + working-directory: build + run: ctest -C ${{ matrix.build_type }} --output-on-failure diff --git a/.gitignore b/.gitignore index 8985f9c..067e77c 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ $RECYCLE.BIN/ ehthumbs.db Thumbs.db *.json +/vc140.pdb diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f03fa9..419c80c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 4.2) + +set(CACHE{CMAKE_EXPERIMENTAL_CXX_IMPORT_STD} TYPE STRING VALUE 451f2fe2-a8a2-47c3-bc32-94786d8fc91b) include(cmake/prelude.cmake) @@ -8,12 +10,14 @@ project( LANGUAGES CXX ) +set(CMAKE_CXX_MODULE_STD 1) + include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) option(BENCHER_ENABLE_JSON "Enable JSON output support via Glaze" OFF) -add_library(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE) +add_library(${PROJECT_NAME}_${PROJECT_NAME} STATIC) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}_${PROJECT_NAME} ) if (MSVC) @@ -29,12 +33,14 @@ endif() set_property(TARGET ${PROJECT_NAME}_${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJECT_NAME}) -target_compile_features(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE cxx_std_23) +target_compile_features(${PROJECT_NAME}_${PROJECT_NAME} PUBLIC cxx_std_23) target_include_directories( ${PROJECT_NAME}_${PROJECT_NAME} ${warning_guard} - INTERFACE "$" + PUBLIC "$" ) +include(cmake/modules-files.cmake) + if(BENCHER_ENABLE_JSON) include(FetchContent) FetchContent_Declare( @@ -45,6 +51,7 @@ if(BENCHER_ENABLE_JSON) ) FetchContent_MakeAvailable(glaze) target_link_libraries(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE glaze::glaze) + target_compile_definitions(BENCHER_ENABLE_JSON) endif() if(NOT CMAKE_SKIP_INSTALL_RULES) @@ -53,4 +60,4 @@ endif() if (PROJECT_IS_TOP_LEVEL) include(cmake/dev-mode.cmake) -endif() \ No newline at end of file +endif() diff --git a/cmake/install-config.cmake b/cmake/install-config.cmake index e9af6c2..f83ace5 100644 --- a/cmake/install-config.cmake +++ b/cmake/install-config.cmake @@ -1 +1,3 @@ -include("${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Targets.cmake") \ No newline at end of file +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake index 261fa29..449462e 100644 --- a/cmake/install-rules.cmake +++ b/cmake/install-rules.cmake @@ -1,58 +1,62 @@ -if(PROJECT_IS_TOP_LEVEL) - set(CMAKE_INSTALL_INCLUDEDIR include/${PROJECT_NAME} CACHE PATH "") -endif() - -# Project is configured with no languages, so tell GNUInstallDirs the lib dir -set(CMAKE_INSTALL_LIBDIR lib CACHE PATH "") - include(CMakePackageConfigHelpers) include(GNUInstallDirs) # find_package() call for consumers to find this project set(package ${PROJECT_NAME}) +set(package_bmidir "${CMAKE_INSTALL_LIBDIR}/${package}/bmi") -install( - DIRECTORY include/ - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - COMPONENT ${PROJECT_NAME}_Development +# Allow package maintainers to freely override the path for the configs +set( + ${PROJECT_NAME}_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/${package}" + CACHE PATH "CMake package config location relative to the install prefix" ) +mark_as_advanced(${PROJECT_NAME}_INSTALL_CMAKEDIR) +set(package_cmakedir "${${PROJECT_NAME}_INSTALL_CMAKEDIR}") install( TARGETS ${PROJECT_NAME}_${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets + FILE_SET bencher_headers + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT ${PROJECT_NAME}_Development + FILE_SET CXX_MODULES + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT ${PROJECT_NAME}_Development + CXX_MODULES_BMI + DESTINATION "${package_bmidir}" + COMPONENT ${PROJECT_NAME}_Development INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) +configure_package_config_file( + cmake/install-config.cmake + "${PROJECT_BINARY_DIR}/${package}Config.cmake" + INSTALL_DESTINATION "${package_cmakedir}" +) + write_basic_package_version_file( "${package}ConfigVersion.cmake" COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT ) -# Allow package maintainers to freely override the path for the configs -set( - zb8_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/${package}" - CACHE PATH "CMake package config location relative to the install prefix" -) -mark_as_advanced(${PROJECT_NAME}_INSTALL_CMAKEDIR) - install( - FILES cmake/install-config.cmake - DESTINATION "${zb8_INSTALL_CMAKEDIR}" - RENAME "${package}Config.cmake" + FILES "${PROJECT_BINARY_DIR}/${package}Config.cmake" + DESTINATION "${package_cmakedir}" COMPONENT ${PROJECT_NAME}_Development ) install( FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" - DESTINATION "${zb8_INSTALL_CMAKEDIR}" + DESTINATION "${package_cmakedir}" COMPONENT ${PROJECT_NAME}_Development ) install( EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}:: - DESTINATION "${zb8_INSTALL_CMAKEDIR}" + DESTINATION "${package_cmakedir}" + CXX_MODULES_DIRECTORY cxx-modules COMPONENT ${PROJECT_NAME}_Development ) diff --git a/cmake/modules-files.cmake b/cmake/modules-files.cmake new file mode 100644 index 0000000..743b08e --- /dev/null +++ b/cmake/modules-files.cmake @@ -0,0 +1,26 @@ +target_sources(bencher_bencher + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS modules + FILES + modules/bencher/bar_chart.ixx + modules/bencher/bencher.ixx + modules/bencher/cache_clearer.ixx + modules/bencher/diagnostics.ixx + modules/bencher/do_not_optimize.ixx + modules/bencher/event_counter.ixx + modules/bencher/file.ixx + modules/bencher/json.ixx + modules/bencher/line_chart.ixx + modules/bencher/radar_chart.ixx +) + +# counters +target_sources(bencher_bencher + PUBLIC + FILE_SET CXX_MODULES + FILES + modules/bencher/counters/apple_arm_perf_events.ixx + modules/bencher/counters/linux_perf_events.ixx + modules/bencher/counters/windows_perf_events.ixx +) diff --git a/include/bencher/event_counter.hpp b/include/bencher/event_counter.hpp deleted file mode 100644 index bdd5a11..0000000 --- a/include/bencher/event_counter.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#if !defined(_MSC_VER) -#include -#endif - -#include -#include - -#include "bencher/config.hpp" -#include "bencher/counters/apple_arm_perf_events.hpp" -#include "bencher/counters/linux_perf_events.hpp" -#include "bencher/counters/windows_perf_events.hpp" - -namespace bencher -{ - struct event_count - { - double elapsed_ns() const noexcept { return std::chrono::duration(elapsed).count(); } - - std::optional missed_branches{}; - uint64_t bytes_processed{}; - std::optional instructions{}; - std::chrono::duration elapsed{}; - std::optional branches{}; - std::optional cycles{}; - }; - - using event_collector = event_collector_type; -} diff --git a/include/bencher/file.hpp b/include/bencher/file.hpp deleted file mode 100644 index cad82d6..0000000 --- a/include/bencher/file.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace bencher -{ - inline bool save_file(const std::string& contents, const std::string& path) - { - std::ofstream file(path.data()); - if (not file) { - return false; - } - file.write(contents.data(), static_cast(contents.size())); - return true; - } -} diff --git a/include/bencher/bar_chart.hpp b/modules/bencher/bar_chart.ixx similarity index 95% rename from include/bencher/bar_chart.hpp rename to modules/bencher/bar_chart.ixx index db2636a..b7d319a 100644 --- a/include/bencher/bar_chart.hpp +++ b/modules/bencher/bar_chart.ixx @@ -1,16 +1,14 @@ -#pragma once +// bar_chart.ixx +export module bencher.bar_chart; -#include -#include -#include -#include // For exception handling -#include -#include +import std; + +using std::size_t; namespace bencher { - struct RGB + export struct RGB { int r{}; int g{}; @@ -18,7 +16,7 @@ namespace bencher }; // Utility function to convert hex color string to RGB components - inline RGB hex_to_rgb(const std::string& hex) + export inline RGB hex_to_rgb(const std::string& hex) { RGB color{0, 0, 0}; @@ -45,13 +43,13 @@ namespace bencher } // Utility function to convert RGB components back to hex color - inline std::string rgb_to_hex(const RGB& color) + export inline std::string rgb_to_hex(const RGB& color) { return std::format("#{0:02X}{1:02X}{2:02X}", color.r & 0xFF, color.g & 0xFF, color.b & 0xFF); } // Utility function to darken a color by a certain percentage - inline std::string darken_color(const std::string& hex, double percentage) + export inline std::string darken_color(const std::string& hex, double percentage) { RGB color = hex_to_rgb(hex); @@ -68,7 +66,7 @@ namespace bencher return rgb_to_hex(color); } - namespace themes + export namespace themes { inline std::vector bright = { "#4CAF50", // Green @@ -97,7 +95,7 @@ namespace bencher }; } - inline std::string xml_escape(const std::string& s) + export inline std::string xml_escape(const std::string& s) { std::string result; result.reserve(s.size()); @@ -125,7 +123,7 @@ namespace bencher return result; } - struct chart_config + export struct chart_config { double chart_width = 1000; double chart_height = 600; @@ -148,7 +146,7 @@ namespace bencher std::string background_color = "#FFFFFF"; // Background color for the chart }; - inline std::string generate_bar_chart_svg(const std::vector& names, const std::vector& data, + export inline std::string generate_bar_chart_svg(const std::vector& names, const std::vector& data, const chart_config& cfg) { // Ensure both vectors have the same size diff --git a/include/bencher/bencher.hpp b/modules/bencher/bencher.ixx similarity index 95% rename from include/bencher/bencher.hpp rename to modules/bencher/bencher.ixx index fe4c212..e689140 100644 --- a/include/bencher/bencher.hpp +++ b/modules/bencher/bencher.ixx @@ -1,28 +1,20 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bencher/cache_clearer.hpp" +// bencher.ixx +module; #include "bencher/config.hpp" -#include "bencher/do_not_optimize.hpp" -#include "bencher/event_counter.hpp" -#include "bencher/file.hpp" +export module bencher; + +import std; + +export import bencher.cache_clearer; +export import bencher.do_not_optimize; +export import bencher.event_counter; +export import bencher.file; + +using std::size_t; namespace bencher { - struct performance_metrics + export struct performance_metrics { // Throughput in units/sec (defaults to MB/s when stage uses bytes + 1024^2 divisor) double throughput_mb_per_sec{}; @@ -32,7 +24,7 @@ namespace bencher std::optional cycles_percentage_deviation; std::optional instructions_per_execution; std::optional branch_misses_per_execution; - std::optional total_iteration_count; + std::optional total_iteration_count; std::optional instructions_per_cycle; std::optional branches_per_execution; std::optional instructions_per_byte; @@ -52,14 +44,14 @@ namespace bencher namespace stats { // Calculate mean of a vector of doubles - inline double mean(const std::vector& data) + export inline double mean(const std::vector& data) { if (data.empty()) throw std::invalid_argument("Data vector is empty."); double sum = std::accumulate(data.begin(), data.end(), 0.0); return sum / double(data.size()); } - inline double median(std::vector data) + export inline double median(std::vector data) { if (data.empty()) throw std::invalid_argument("Data vector is empty."); size_t n = data.size(); @@ -77,7 +69,7 @@ namespace bencher } } - inline double standard_deviation(const std::vector& data, double mean_val) + export inline double standard_deviation(const std::vector& data, double mean_val) { if (data.size() < 2) throw std::invalid_argument("At least two data points are required."); double accum = 0.0; @@ -89,7 +81,7 @@ namespace bencher } // Calculate Median Absolute Deviation (MAD) - inline double median_absolute_deviation(const std::vector& data, double median_val) + export inline double median_absolute_deviation(const std::vector& data, double median_val) { std::vector deviations; deviations.reserve(data.size()); @@ -103,18 +95,18 @@ namespace bencher constexpr double z_score_95 = 1.96; } - struct stage + export struct stage { std::string name{}; - uint64_t min_execution_count = 30; - uint64_t max_execution_count = 1000; + std::uint64_t min_execution_count = 30; + std::uint64_t max_execution_count = 1000; // Threshold for relative half-width of 95% CI, e.g. 2.0% means we stop // once the ±CI is within ±2% of the mean throughput. double confidence_interval_threshold = 2.0; // Warmup duration in milliseconds to stabilize CPU frequency - uint32_t warmup_duration_ms = 1000; + std::uint32_t warmup_duration_ms = 1000; // If true, evict L1 cache between runs for cold-cache measurements // Set to false for warm-cache (steady-state) benchmarks @@ -219,8 +211,8 @@ namespace bencher // bytes_processed comes from the last invocation, which is // intentionally per-invocation (not cumulative) since elapsed // and counters are also divided by batch_size below. - std::ignore = collector.start(events[i], [&]() -> uint64_t { - uint64_t result = 0; + std::ignore = collector.start(events[i], [&]() -> std::uint64_t { + std::uint64_t result = 0; for (size_t b = 0; b < batch_size; ++b) { result = function(args...); } @@ -374,8 +366,8 @@ namespace bencher // bytes_processed is intentionally per-invocation (not cumulative) // since elapsed and counters are also divided by batch_size below. - std::ignore = collector.start(events[i], [&]() -> uint64_t { - uint64_t result = 0; + std::ignore = collector.start(events[i], [&]() -> std::uint64_t { + std::uint64_t result = 0; for (auto& s : states) { result = function(s); } diff --git a/include/bencher/cache_clearer.hpp b/modules/bencher/cache_clearer.ixx similarity index 97% rename from include/bencher/cache_clearer.hpp rename to modules/bencher/cache_clearer.ixx index 5f7cd1c..1208c7e 100644 --- a/include/bencher/cache_clearer.hpp +++ b/modules/bencher/cache_clearer.ixx @@ -1,9 +1,6 @@ -#pragma once - -#include - +// cache_clearer.ixx +module; #include "bencher/config.hpp" - #if defined(BENCH_WIN) #include @@ -17,6 +14,9 @@ #include #include #endif +export module bencher.cache_clearer; + +import std; namespace bencher { @@ -100,7 +100,7 @@ namespace bencher } #endif - struct cache_clearer + export struct cache_clearer { inline static size_t cache_line_size = 64; inline static size_t l1_cache_size{get_l1_cache_size()}; diff --git a/include/bencher/config.hpp b/modules/bencher/config.hpp similarity index 98% rename from include/bencher/config.hpp rename to modules/bencher/config.hpp index 0988e81..df581eb 100644 --- a/include/bencher/config.hpp +++ b/modules/bencher/config.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #if defined(__clang__) || (defined(__GNUC__) && defined(__llvm__)) || (defined(__APPLE__) && defined(__clang__)) #define BENCH_CLANG 1 #elif defined(_MSC_VER) diff --git a/include/bencher/counters/apple_arm_perf_events.hpp b/modules/bencher/counters/apple_arm_perf_events.ixx similarity index 97% rename from include/bencher/counters/apple_arm_perf_events.hpp rename to modules/bencher/counters/apple_arm_perf_events.ixx index 6a47318..53a8af6 100644 --- a/include/bencher/counters/apple_arm_perf_events.hpp +++ b/modules/bencher/counters/apple_arm_perf_events.ixx @@ -1,24 +1,22 @@ -#pragma once - +// apple_arm_perf_events.ixx +module; #include "bencher/config.hpp" -#if defined(BENCH_MAC) - +#ifdef BENCH_MAC #include #include #include #include #include +#endif +export module bencher.counters.apple; +#if defined(BENCH_MAC) + +using std::size_t; +using std::int32_t; +using std::uint32_t; -#include -#include -#include -#include -#include -#include -#include -#include -#include +import std; // ----------------------------------------------------------------------------- /** Customize error handling **/ @@ -134,7 +132,7 @@ namespace bencher inline constexpr uint32_t KPC_CLASS_RAWPMU_MASK = 1u << KPC_CLASS_RAWPMU; inline constexpr size_t KPC_MAX_COUNTERS = 32; - using kpc_config_t = uint64_t; + using kpc_config_t = std::uint64_t; // ----------------------------------------------------------------------------- // Used kperf function pointers @@ -145,7 +143,7 @@ namespace bencher inline int32_t (*kpc_set_config)(uint32_t classes, kpc_config_t* config) = nullptr; inline int32_t (*kpc_set_counting)(uint32_t classes) = nullptr; inline int32_t (*kpc_set_thread_counting)(uint32_t classes) = nullptr; - inline int32_t (*kpc_get_thread_counters)(uint32_t tid, uint32_t buf_count, uint64_t* buf) = nullptr; + inline int32_t (*kpc_get_thread_counters)(uint32_t tid, uint32_t buf_count, std::uint64_t* buf) = nullptr; // ----------------------------------------------------------------------------- // Used kperfdata (kpep) function pointers and structures @@ -197,7 +195,7 @@ namespace bencher size_t* ev_map; size_t* ev_idx; uint32_t* flags; - uint64_t* kpc_periods; + std::uint64_t* kpc_periods; size_t event_count; size_t counter_count; uint32_t classes; @@ -376,8 +374,8 @@ namespace bencher inline std::array regs{}; inline std::array counter_map{}; - inline std::array counters_0{}; - inline std::array counters_1{}; + inline std::array counters_0{}; + inline std::array counters_1{}; inline constexpr size_t ev_count = profile_events.size(); inline std::error_condition setup_performance_counters() @@ -519,7 +517,7 @@ namespace bencher } // Example collector for benchmarking - template + export template struct event_collector_type { performance_counters diff{}; diff --git a/include/bencher/counters/linux_perf_events.hpp b/modules/bencher/counters/linux_perf_events.ixx similarity index 91% rename from include/bencher/counters/linux_perf_events.hpp rename to modules/bencher/counters/linux_perf_events.ixx index 92bc7b1..a80ca52 100644 --- a/include/bencher/counters/linux_perf_events.hpp +++ b/modules/bencher/counters/linux_perf_events.ixx @@ -1,22 +1,23 @@ -#pragma once +// linux_perf_events.ixx +module; #include "bencher/config.hpp" -#if defined(BENCH_LINUX) - +#ifdef BENCH_LINUX +#include #include #include #include #include #include +#endif +export module bencher.counters.linux_perf_events; +#if defined(BENCH_LINUX) -#include -#include -#include -#include -#include -#include -#include +import std; + +using std::uint32_t; +using std::int32_t; namespace bencher { @@ -40,8 +41,8 @@ namespace bencher class linux_events { protected: - std::vector temp_result_vec{}; - std::vector ids{}; + std::vector temp_result_vec{}; + std::vector ids{}; std::vector fds{}; // All file descriptors perf_event_attr attribs{}; bool working{true}; @@ -52,7 +53,7 @@ namespace bencher public: linux_events(std::vector config_vec) { - memset(&attribs, 0, sizeof(attribs)); + std::memset(&attribs, 0, sizeof(attribs)); attribs.type = TYPE; attribs.size = sizeof(attribs); attribs.disabled = 1; @@ -109,7 +110,7 @@ namespace bencher } } - BENCH_ALWAYS_INLINE void end(std::vector& results) + BENCH_ALWAYS_INLINE void end(std::vector& results) { if (fd != -1) { if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { @@ -141,7 +142,7 @@ namespace bencher } }; - template + export template struct event_collector_type : public linux_events<> { event_collector_type() @@ -163,12 +164,12 @@ namespace bencher [[nodiscard]] BENCH_ALWAYS_INLINE std::error_condition start(event_count& count, Function&& function, FuncArgs&&... func_args) { - std::vector results{}; + std::vector results{}; if (has_events()) { linux_events<>::start(); } const auto start_clock = std::chrono::steady_clock::now(); - volatile uint64_t cycleStart = rdtsc(); + volatile std::uint64_t cycleStart = rdtsc(); if constexpr (std::is_void_v>) { std::forward(function)(std::forward(func_args)...); count.bytes_processed = 0; @@ -176,7 +177,7 @@ namespace bencher else { count.bytes_processed = std::forward(function)(std::forward(func_args)...); } - volatile uint64_t cycleEnd = rdtsc(); + volatile std::uint64_t cycleEnd = rdtsc(); const auto end_clock = std::chrono::steady_clock::now(); count.elapsed = end_clock - start_clock; if (has_events()) { diff --git a/include/bencher/counters/windows_perf_events.hpp b/modules/bencher/counters/windows_perf_events.ixx similarity index 78% rename from include/bencher/counters/windows_perf_events.hpp rename to modules/bencher/counters/windows_perf_events.ixx index d708605..0d3949f 100644 --- a/include/bencher/counters/windows_perf_events.hpp +++ b/modules/bencher/counters/windows_perf_events.ixx @@ -1,23 +1,20 @@ -#pragma once - +// windows_perf_events.ixx +module; #include "bencher/config.hpp" +#ifdef BENCH_WIN +#include +#endif +export module bencher.counters.windows; #if defined(BENCH_WIN) -#include // for errno -#include -#include // for memset -#include -#include -#include -#include -#include +import std; namespace bencher { BENCH_ALWAYS_INLINE size_t rdtsc() { return __rdtsc(); } - template + export template struct event_collector_type { [[nodiscard]] std::error_condition error() @@ -30,7 +27,7 @@ namespace bencher FuncArgs&&... func_args) { const auto start_clock = std::chrono::steady_clock::now(); - volatile uint64_t cycleStart = rdtsc(); + volatile std::uint64_t cycleStart = rdtsc(); if constexpr (std::is_void_v>) { std::forward(function)(std::forward(func_args)...); count.bytes_processed = 0; @@ -38,7 +35,7 @@ namespace bencher else { count.bytes_processed = std::forward(function)(std::forward(func_args)...); } - volatile uint64_t cycleEnd = rdtsc(); + volatile std::uint64_t cycleEnd = rdtsc(); const auto end_clock = std::chrono::steady_clock::now(); count.cycles.emplace(cycleEnd - cycleStart); count.elapsed = end_clock - start_clock; diff --git a/include/bencher/diagnostics.hpp b/modules/bencher/diagnostics.ixx similarity index 96% rename from include/bencher/diagnostics.hpp rename to modules/bencher/diagnostics.ixx index c98389a..545d4e3 100644 --- a/include/bencher/diagnostics.hpp +++ b/modules/bencher/diagnostics.ixx @@ -1,13 +1,16 @@ -#pragma once +// diagnostics.ixx +export module bencher.diagnostics; -#include +import std; -#include "bencher/bar_chart.hpp" -#include "bencher/bencher.hpp" +import bencher; +import bencher.bar_chart; + +using std::size_t; namespace bencher { - inline std::string format_bar_chart(const std::vector& names, const std::vector& values) + export inline std::string format_bar_chart(const std::vector& names, const std::vector& values) { // Validate input sizes if (names.size() != values.size() || names.empty()) { @@ -87,7 +90,7 @@ namespace bencher std::cout << format_bar_chart(names, values); } - inline std::string bar_chart(const bencher::stage& stage, chart_config cfg = {}) + export inline std::string bar_chart(const bencher::stage& stage, chart_config cfg = {}) { const auto& results = stage.results; std::vector names; @@ -226,7 +229,7 @@ namespace bencher } } - inline std::string to_markdown(const bencher::stage& stage) + export inline std::string to_markdown(const bencher::stage& stage) { std::vector metrics = stage.results; std::sort(metrics.begin(), metrics.end(), std::greater{}); diff --git a/include/bencher/do_not_optimize.hpp b/modules/bencher/do_not_optimize.ixx similarity index 84% rename from include/bencher/do_not_optimize.hpp rename to modules/bencher/do_not_optimize.ixx index 05f3e0f..863e070 100644 --- a/include/bencher/do_not_optimize.hpp +++ b/modules/bencher/do_not_optimize.ixx @@ -1,8 +1,9 @@ -#pragma once - -#include - +// do_not_optimize.ixx +module; #include "bencher/config.hpp" +export module bencher.do_not_optimize; + +import std; namespace bencher { @@ -25,28 +26,28 @@ namespace bencher #if defined(BENCH_MSVC) #define BENCH_DO_NOT_OPTIMIZE(value) \ use_char_pointer(&reinterpret_cast(value)); \ - _ReadWriteBarrier(); + std::atomic_signal_fence(std::memory_order_seq_cst); #elif defined(BENCH_CLANG) #define BENCH_DO_NOT_OPTIMIZE(value) asm volatile("" : "+r,m"(value) : : "memory"); #else #define BENCH_DO_NOT_OPTIMIZE(value) asm volatile("" : "+m,r"(value) : : "memory"); #endif - template + export template BENCH_ALWAYS_INLINE void do_not_optimize(T&& value) { const auto* value_ptr = &value; BENCH_DO_NOT_OPTIMIZE(value_ptr); } - template + export template BENCH_ALWAYS_INLINE void do_not_optimize(Function&& value, Args&&... args) { std::forward(value)(std::forward(args)...); BENCH_DO_NOT_OPTIMIZE(value); } - template + export template BENCH_ALWAYS_INLINE void do_not_optimize(Function&& value, Args&&... args) { auto result_val = std::forward(value)(std::forward(args)...); diff --git a/modules/bencher/event_counter.ixx b/modules/bencher/event_counter.ixx new file mode 100644 index 0000000..991594f --- /dev/null +++ b/modules/bencher/event_counter.ixx @@ -0,0 +1,35 @@ +// event_counter.ixx +module; +#if !defined(_MSC_VER) +#include +#endif + +#include "bencher/config.hpp" +export module bencher.event_counter; + +import std; + +#ifdef BENCH_WIN +import bencher.counters.windows; +#elif defined(BENCH_LINUX) +import bencher.counters.linux_perf_events; +#elif defined(BENCH_MAC) +import bencher.counters.apple; +#endif + +namespace bencher +{ + export struct event_count + { + double elapsed_ns() const noexcept { return std::chrono::duration(elapsed).count(); } + + std::optional missed_branches{}; + std::uint64_t bytes_processed{}; + std::optional instructions{}; + std::chrono::duration elapsed{}; + std::optional branches{}; + std::optional cycles{}; + }; + + export using event_collector = event_collector_type; +} diff --git a/modules/bencher/file.ixx b/modules/bencher/file.ixx new file mode 100644 index 0000000..fc88a42 --- /dev/null +++ b/modules/bencher/file.ixx @@ -0,0 +1,17 @@ +// file.ixx +export module bencher.file; + +import std; + +namespace bencher +{ + export inline bool save_file(const std::string& contents, const std::string& path) + { + std::ofstream file(path.data()); + if (not file) { + return false; + } + file.write(contents.data(), static_cast(contents.size())); + return true; + } +} diff --git a/include/bencher/json.hpp b/modules/bencher/json.ixx similarity index 60% rename from include/bencher/json.hpp rename to modules/bencher/json.ixx index bbfc1f9..26e13fd 100644 --- a/include/bencher/json.hpp +++ b/modules/bencher/json.ixx @@ -1,10 +1,13 @@ -#pragma once +// json.ixx +export module bencher.json; -#if __has_include() +#ifdef BENCHER_ENABLE_JSON -#include +import std; -#include "bencher/bencher.hpp" +import bencher; + +import glaze; namespace bencher { @@ -14,17 +17,16 @@ namespace bencher std::vector results{}; }; - [[nodiscard]] inline std::string to_json(const stage& s) + export [[nodiscard]] inline std::string to_json(const stage& s) { stage_result output{s.name, s.results}; return glz::write_json(output).value_or("{}"); } - [[nodiscard]] inline std::string to_json_pretty(const stage& s) + export [[nodiscard]] inline std::string to_json_pretty(const stage& s) { stage_result output{s.name, s.results}; return glz::write(output).value_or("{}"); } } - -#endif +#endif \ No newline at end of file diff --git a/include/bencher/line_chart.hpp b/modules/bencher/line_chart.ixx similarity index 98% rename from include/bencher/line_chart.hpp rename to modules/bencher/line_chart.ixx index 8800e95..b486623 100644 --- a/include/bencher/line_chart.hpp +++ b/modules/bencher/line_chart.ixx @@ -1,11 +1,11 @@ -#pragma once +// line_chart.ixx +export module bencher.line_chart; -#include -#include -#include -#include +import std; -#include "bar_chart.hpp" // Reuse chart_config, color utilities, themes +import bencher.bar_chart; + +using std::size_t; namespace bencher { diff --git a/include/bencher/radar_chart.hpp b/modules/bencher/radar_chart.ixx similarity index 99% rename from include/bencher/radar_chart.hpp rename to modules/bencher/radar_chart.ixx index 5f8b6c5..15b3fec 100644 --- a/include/bencher/radar_chart.hpp +++ b/modules/bencher/radar_chart.ixx @@ -1,14 +1,11 @@ -#pragma once +// radar_chart.ixx +export module bencher.radar_chart; -#include -#include -#include -#include -#include -#include -#include +import std; -#include "bar_chart.hpp" // xml_escape, themes, RGB +import bencher.bar_chart; + +using std::size_t; namespace bencher { diff --git a/src/main.cpp b/src/main.cpp index 9c88e1a..b6488b1 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ -#include "bencher/bencher.hpp" -#include "bencher/diagnostics.hpp" -#include "bencher/bar_chart.hpp" -#include "bencher/file.hpp" +import bencher; +import bencher.diagnostics; +import bencher.bar_chart; +import bencher.file; int main() { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 14fab2d..8a40f2e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,8 @@ FetchContent_Declare( GIT_SHALLOW TRUE ) +set (UT_ENABLE_MODULES ON) + message(STATUS "Fetching dependencies...") set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL TRUE) set(CMAKE_SKIP_INSTALL_RULES ON CACHE BOOL "" FORCE) @@ -19,14 +21,13 @@ add_library(test_common INTERFACE) target_compile_features(test_common INTERFACE cxx_std_23) target_link_libraries(test_common INTERFACE ut::ut bencher::bencher) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(test_common INTERFACE -fno-rtti) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options(test_common INTERFACE -Wall -Wextra -pedantic $<$:-Werror>) else() target_compile_options(test_common INTERFACE -Wall -Wextra -pedantic) endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - target_compile_options(test_common INTERFACE /GR- /bigobj) + target_compile_options(test_common INTERFACE /bigobj) target_compile_options(test_common INTERFACE /W4 /wd4459 /wd4805) endif() diff --git a/tests/unit_test/unit_test.cpp b/tests/unit_test/unit_test.cpp index d2e1b5f..96c0a7e 100644 --- a/tests/unit_test/unit_test.cpp +++ b/tests/unit_test/unit_test.cpp @@ -1,8 +1,13 @@ -#include "bencher/bar_chart.hpp" -#include "bencher/bencher.hpp" -#include "bencher/diagnostics.hpp" -#include "bencher/file.hpp" -#include "ut/ut.hpp" +import bencher; +import bencher.bar_chart; +import bencher.diagnostics; +import bencher.file; + +import ut; + +import std; + +using std::size_t; using namespace ut; @@ -350,7 +355,7 @@ suite bar_chart_tests = [] { "rgb_to_hex"_test = [] { bencher::RGB color{255, 128, 64}; - auto hex = rgb_to_hex(color); + auto hex = bencher::rgb_to_hex(color); expect(hex == "#FF8040"); }; @@ -611,7 +616,7 @@ suite event_collector_tests = [] { bencher::event_collector collector; bencher::event_count count; - constexpr uint64_t expected_bytes = 42; + constexpr std::uint64_t expected_bytes = 42; (void)collector.start(count, [] { return expected_bytes; }); expect(count.bytes_processed == expected_bytes); From 623f7ecfb429422b53cacb364dbde2969efc6417 Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Tue, 16 Jun 2026 23:58:22 +0100 Subject: [PATCH 2/3] CI --- .github/workflows/clang.yml | 34 --------------- .github/workflows/gcc.yml | 33 --------------- .github/workflows/linux.yml | 46 +++++++++++++++++++++ .github/workflows/macos.yml | 31 ++++++++------ .github/workflows/msvc.yml | 21 +++++----- .gitignore | 1 + CMakeLists.txt | 4 +- CMakePresets.json | 82 +++++++++++++++++++++++++++++++++++++ 8 files changed, 161 insertions(+), 91 deletions(-) delete mode 100644 .github/workflows/clang.yml delete mode 100644 .github/workflows/gcc.yml create mode 100644 .github/workflows/linux.yml create mode 100644 CMakePresets.json diff --git a/.github/workflows/clang.yml b/.github/workflows/clang.yml deleted file mode 100644 index 195e787..0000000 --- a/.github/workflows/clang.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Clang - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - clang: - runs-on: ubuntu-26.04 - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - clang_version: [21, 22] - - steps: - - uses: actions/checkout@v6 - - - name: Configure CMake - run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DCMAKE_CXX_COMPILER=clang++-${{ matrix.clang_version }} \ - -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ - -DBUILD_TESTING=ON - - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $(nproc) - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml deleted file mode 100644 index be73d2f..0000000 --- a/.github/workflows/gcc.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: GCC - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - gcc: - runs-on: ubuntu-26.04 - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - gcc_version: [15] - - steps: - - uses: actions/checkout@v6 - - - name: Configure CMake - run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DCMAKE_CXX_COMPILER=g++-${{ matrix.gcc_version }} \ - -DBUILD_TESTING=ON - - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $(nproc) - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..0b4491c --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,46 @@ +name: Linux + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + linux: + name: ${{ matrix.compiler }} ${{ matrix.build_type }} + runs-on: ${{ matrix.runs_on }} + strategy: + fail-fast: false + matrix: + include: + - build_type: Debug + runs_on: ubuntu-26.04 + compiler: gcc-15 + cc: gcc-15 + cxx: g++-15 + configure_preset: "linux" + build_preset: "linux-build" + test_preset: "linux-test" + - build_type: Debug + runs_on: ubuntu-26.04 + compiler: clang-22 + cc: clang-22 + cxx: clang++-22 + configure_preset: "linux" + build_preset: "linux-build" + test_preset: "linux-test" + + steps: + - uses: actions/checkout@v6 + + - name: "Build & Test" + uses: lukka/run-cmake@v10 + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + with: + configurePreset: ${{ matrix.configure_preset }} + configurePresetAdditionalArgs: "['-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}']" + buildPreset: ${{ matrix.build_preset }} + testPreset: ${{ matrix.test_preset }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7694037..3238024 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -8,24 +8,31 @@ on: jobs: macos: - runs-on: macos-latest + runs-on: macos-26 strategy: fail-fast: false matrix: - build_type: [Debug, Release] + build_type: [Debug] + llvm_version: [20] + preset: + - configure_preset: "macos" + build_preset: "macos-build" + test_preset: "macos-test" steps: - uses: actions/checkout@v6 - - name: Configure CMake + - name: Locate Homebrew LLVM + id: llvm run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DBUILD_TESTING=ON + llvm_prefix="$(brew --prefix llvm@${{ matrix.llvm_version }})" + "$llvm_prefix/bin/clang++" --version + echo "cxx=$llvm_prefix/bin/clang++" >> "$GITHUB_OUTPUT" - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $(sysctl -n hw.ncpu) - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure + - name: "Build & Test" + uses: lukka/run-cmake@v10 + with: + configurePreset: ${{ matrix.preset.configure_preset }} + configurePresetAdditionalArgs: "['-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}', '-DCMAKE_CXX_COMPILER=${{ steps.llvm.outputs.cxx }}']" + buildPreset: ${{ matrix.preset.build_preset }} + testPreset: ${{ matrix.preset.test_preset }} diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 720bd49..b8a2edc 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -12,18 +12,17 @@ jobs: strategy: fail-fast: false matrix: - build_type: [Debug, Release] + preset: + - configure_preset: "x64" + build_preset: "x64-build" + test_preset: "x64-test" steps: - uses: actions/checkout@v6 - - name: Configure CMake - run: | - cmake -B build -DBUILD_TESTING=ON - - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} -j $env:NUMBER_OF_PROCESSORS - - - name: Test - working-directory: build - run: ctest -C ${{ matrix.build_type }} --output-on-failure + - name: "Build & Test" + uses: lukka/run-cmake@v10 + with: + configurePreset: ${{ matrix.preset.configure_preset }} + buildPreset: ${{ matrix.preset.build_preset }} + testPreset: ${{ matrix.preset.test_preset }} diff --git a/.gitignore b/.gitignore index 067e77c..f7f7b7d 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ $RECYCLE.BIN/ ehthumbs.db Thumbs.db *.json +!CMakePresets.json /vc140.pdb diff --git a/CMakeLists.txt b/CMakeLists.txt index 419c80c..9a13183 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,9 @@ project( LANGUAGES CXX ) -set(CMAKE_CXX_MODULE_STD 1) +if(NOT CMAKE_GENERATOR MATCHES "Visual Studio") + set(CMAKE_CXX_MODULE_STD 1) +endif() include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..1b457a2 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,82 @@ +{ + "version": 9, + "configurePresets": [ + { + "name": "windows-base-vs", + "hidden": true, + "generator": "Visual Studio 18 2026", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64", + "inherits": "windows-base-vs", + "cacheVariables": { + "BUILD_TESTING": "ON" + } + }, + { + "name": "linux", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "cacheVariables": { + "BUILD_TESTING": "ON" + } + }, + { + "name": "macos", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "cacheVariables": { + "BUILD_TESTING": "ON" + } + } + ], + "buildPresets": [ + { + "name": "x64-build", + "configurePreset": "x64", + "configuration": "Debug" + }, + { + "name": "linux-build", + "configurePreset": "linux" + }, + { + "name": "macos-build", + "configurePreset": "macos" + } + ], + "testPresets": [ + { + "name": "x64-test", + "configurePreset": "x64", + "configuration": "Debug" + }, + { + "name": "linux-test", + "configurePreset": "linux" + }, + { + "name": "macos-test", + "configurePreset": "macos" + } + ] +} From 5818d46d608fe2e25a277ee0de12a356208d097b Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Wed, 17 Jun 2026 00:00:46 +0100 Subject: [PATCH 3/3] Fix compilation on GCC --- modules/bencher/do_not_optimize.ixx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/bencher/do_not_optimize.ixx b/modules/bencher/do_not_optimize.ixx index 863e070..88e9b37 100644 --- a/modules/bencher/do_not_optimize.ixx +++ b/modules/bencher/do_not_optimize.ixx @@ -16,12 +16,14 @@ namespace bencher template concept invocable_not_void = std::invocable && !std::is_void_v>; - static const volatile void* volatile global_force_escape_pointer; +#if defined(BENCH_MSVC) + const volatile void* volatile global_force_escape_pointer; inline void use_char_pointer(const volatile char* const v) { global_force_escape_pointer = reinterpret_cast(v); } +#endif #if defined(BENCH_MSVC) #define BENCH_DO_NOT_OPTIMIZE(value) \