diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f2ff69..335409f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,9 +9,11 @@ on: jobs: tests: runs-on: ubuntu-latest - steps: + steps: - name: Checkout code uses: actions/checkout@v3 + with: + submodules: "recursive" - name: Cache build uses: actions/cache@v3 @@ -29,7 +31,7 @@ jobs: make -j - name: Run tests - run: | + run: | cd core/build-tests GTEST_OUTPUT=json:test-results/ ctest @@ -54,6 +56,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 + with: + submodules: "recursive" - name: Cache build uses: actions/cache@v3 @@ -73,6 +77,8 @@ jobs: - name: Run the benchmarks uses: CodSpeedHQ/action@main if: matrix.codspeed-mode != 'off' + env: + CODSPEED_PERF_ENABLED: true with: run: examples/google_benchmark_cmake/build/benchmark_example token: ${{ secrets.CODSPEED_TOKEN }} @@ -90,6 +96,8 @@ jobs: runs-on: ${{ matrix.runner }} steps: - uses: actions/checkout@v4 + with: + submodules: "recursive" - name: Set up Bazel uses: bazel-contrib/setup-bazel@0.14.0 @@ -104,10 +112,12 @@ jobs: - name: Build and run benchmarks run: | bazel build //examples/google_benchmark_bazel:my_benchmark --//core:codspeed_mode=${{ matrix.codspeed-mode }} - + - name: Run the benchmarks uses: CodSpeedHQ/action@main if: matrix.codspeed-mode != 'off' + env: + CODSPEED_PERF_ENABLED: true with: run: bazel run //examples/google_benchmark_bazel:my_benchmark --//core:codspeed_mode=${{ matrix.codspeed-mode }} token: ${{ secrets.CODSPEED_TOKEN }} @@ -120,6 +130,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 + with: + submodules: "recursive" - name: Cache build uses: actions/cache@v3 @@ -148,6 +160,8 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 + with: + submodules: "recursive" - name: Set up Bazel uses: bazel-contrib/setup-bazel@0.14.0 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..639e8f0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "core/instrument-hooks"] + path = core/instrument-hooks + url = https://github.com/CodSpeedHQ/instrument-hooks diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..d6e3818 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +exclude: '^(google_benchmark/.*|core/instrument-hooks/.*|.*/build/.*|build/.*|core/include/valgrind\.h|core/include/callgrind\.h)' +files: ^(core|examples)/.*$ + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + - repo: https://github.com/cpp-linter/cpp-linter-hooks + rev: v0.6.1 + hooks: + - id: clang-format + files: \.(cpp|cc|cxx|h|hpp)$ diff --git a/core/BUILD b/core/BUILD index 1769850..0c37e4d 100644 --- a/core/BUILD +++ b/core/BUILD @@ -8,6 +8,26 @@ config_setting( constraint_values = ["@platforms//os:windows"], ) +# Define the instrument-hooks library separately to apply warning suppressions +cc_library( + name = "instrument_hooks", + srcs = ["instrument-hooks/dist/core.c"], + hdrs = glob(["instrument-hooks/includes/*.h"]), + includes = ["instrument-hooks/includes"], + copts = select({ + ":windows": [ + "/wd4101", # unreferenced local variable (equivalent to -Wno-unused-variable) + "/wd4189", # local variable is initialized but not referenced (equivalent to -Wno-unused-but-set-variable) + "/wd4100", # unreferenced formal parameter (equivalent to -Wno-unused-parameter) + ], + "//conditions:default": [ + "-Wno-unused-variable", + "-Wno-unused-parameter", + "-Wno-unused-but-set-variable", + ], + }), +) + # Define the codspeed library cc_library( name = "codspeed", @@ -25,6 +45,7 @@ cc_library( ":walltime_mode": ["CODSPEED_ENABLED", "CODSPEED_WALLTIME"], "//conditions:default": [], }), + deps = [":instrument_hooks"], visibility = ["//visibility:public"], ) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index b8bb567..3d3fd5d 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10) set(CODSPEED_VERSION 1.2.0) -project(codspeed VERSION ${CODSPEED_VERSION} LANGUAGES CXX) +project(codspeed VERSION ${CODSPEED_VERSION} LANGUAGES CXX C) # Specify the C++ standard set(CMAKE_CXX_STANDARD 17) @@ -10,8 +10,39 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # Add the include directory include_directories(include) +include_directories(instrument-hooks/includes) # Add the library +add_library( + instrument_hooks + instrument-hooks/dist/core.c +) + +# Suppress warnings for the instrument_hooks library +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options( + instrument_hooks + PRIVATE + -Wno-maybe-uninitialized + -Wno-unused-variable + -Wno-unused-parameter + -Wno-unused-but-set-variable + -Wno-type-limits + ) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options( + instrument_hooks + PRIVATE + /wd4101 # unreferenced local variable (equivalent to -Wno-unused-variable) + /wd4189 # local variable is initialized but not referenced (equivalent to -Wno-unused-but-set-variable) + /wd4100 # unreferenced formal parameter (equivalent to -Wno-unused-parameter) + /wd4245 # signed/unsigned mismatch + /wd4132 # const object should be initialized + /wd4146 # unary minus operator applied to unsigned type + ) +endif() + +# Add the main library add_library( codspeed src/codspeed.cpp @@ -20,6 +51,9 @@ add_library( src/workspace.cpp ) +# Link instrument_hooks to codspeed +target_link_libraries(codspeed PRIVATE instrument_hooks) + # Version add_compile_definitions(CODSPEED_VERSION="${CODSPEED_VERSION}") @@ -27,6 +61,7 @@ add_compile_definitions(CODSPEED_VERSION="${CODSPEED_VERSION}") target_include_directories( codspeed PUBLIC $ + $ ) # Disable valgrind compilation errors @@ -116,7 +151,7 @@ install( ) install( - TARGETS codspeed + TARGETS codspeed instrument_hooks EXPORT codspeed-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/core/include/measurement.hpp b/core/include/measurement.hpp index 4604d32..9f65fb8 100644 --- a/core/include/measurement.hpp +++ b/core/include/measurement.hpp @@ -2,11 +2,28 @@ #define MEASUREMENT_H #include +#ifdef _WIN32 +#include +#else +#include +#endif #ifdef CODSPEED_INSTRUMENTATION #include "callgrind.h" #endif +extern "C" { +#include "core.h" +} + +inline InstrumentHooks* get_hooks() { + static InstrumentHooks* g_hooks = nullptr; + if (!g_hooks) { + g_hooks = instrument_hooks_init(); + } + return g_hooks; +} + inline std::string get_version() { #ifdef CODSPEED_VERSION return {CODSPEED_VERSION}; @@ -16,29 +33,45 @@ inline std::string get_version() { } #ifdef CODSPEED_INSTRUMENTATION -inline bool measurement_is_instrumented() { return RUNNING_ON_VALGRIND; } +inline bool measurement_is_instrumented() { + return instrument_hooks_is_instrumented(get_hooks()); +} inline void measurement_set_metadata() { - std::string metadata = "Metadata: codspeed-cpp " + get_version(); - CALLGRIND_DUMP_STATS_AT(metadata.c_str()); + std::string version = get_version(); + instrument_hooks_set_integration(get_hooks(), "codspeed-cpp", + version.c_str()); } __attribute__((always_inline)) inline void measurement_start() { + // Keep the callgrind macros here, so that they are properly inlined. + // Otherwise, we have an additional function call overhead to the + // instrument-hooks library. CALLGRIND_ZERO_STATS; CALLGRIND_START_INSTRUMENTATION; + + instrument_hooks_start_benchmark(get_hooks()); } __attribute__((always_inline)) inline void measurement_stop( - const std::string &name) { + const std::string& name) { CALLGRIND_STOP_INSTRUMENTATION; - CALLGRIND_DUMP_STATS_AT(name.c_str()); + + instrument_hooks_stop_benchmark(get_hooks()); + +#ifdef _WIN32 + auto current_pid = _getpid(); +#else + auto current_pid = getpid(); +#endif + instrument_hooks_executed_benchmark(get_hooks(), current_pid, name.c_str()); }; #else // Stub implementations for non-instrumentation builds inline bool measurement_is_instrumented() { return false; } inline void measurement_set_metadata() {} inline void measurement_start() {} -inline void measurement_stop(const std::string &name) { (void)name; } +inline void measurement_stop(const std::string& name) { (void)name; } #endif #endif // MEASUREMENT_H diff --git a/core/instrument-hooks b/core/instrument-hooks new file mode 160000 index 0000000..9db4a71 --- /dev/null +++ b/core/instrument-hooks @@ -0,0 +1 @@ +Subproject commit 9db4a713939644bef0f7752fcd6a6e844ab8468d diff --git a/core/src/utils.h b/core/src/utils.h index 42fefc2..302a1c8 100644 --- a/core/src/utils.h +++ b/core/src/utils.h @@ -10,4 +10,4 @@ std::string safe_getenv(const char* var_name); } // namespace codspeed -#endif // CODSPEED_UTILS_H \ No newline at end of file +#endif // CODSPEED_UTILS_H diff --git a/examples/google_benchmark_bazel/sleep_bench.hpp b/examples/google_benchmark_bazel/sleep_bench.hpp new file mode 120000 index 0000000..a53516b --- /dev/null +++ b/examples/google_benchmark_bazel/sleep_bench.hpp @@ -0,0 +1 @@ +../google_benchmark_cmake/sleep_bench.hpp \ No newline at end of file diff --git a/examples/google_benchmark_cmake/fixture_bench.hpp b/examples/google_benchmark_cmake/fixture_bench.hpp index ff2d4e3..804e33a 100644 --- a/examples/google_benchmark_cmake/fixture_bench.hpp +++ b/examples/google_benchmark_cmake/fixture_bench.hpp @@ -33,8 +33,8 @@ BENCHMARK_TEMPLATE_F(MyTemplatedFixture, IntTest, int)(benchmark::State &st) { for (auto _ : st) { } } -BENCHMARK_TEMPLATE_DEFINE_F(MyTemplatedFixture, DoubleTest, - double)(benchmark::State &st) { +BENCHMARK_TEMPLATE_DEFINE_F(MyTemplatedFixture, DoubleTest, double) +(benchmark::State &st) { for (auto _ : st) { } } @@ -56,8 +56,8 @@ BENCHMARK_REGISTER_F(MyTemplate1, TestA); template class MyTemplate2 : public benchmark::Fixture {}; -BENCHMARK_TEMPLATE2_DEFINE_F(MyTemplate2, TestB, int, - double)(benchmark::State &st) { +BENCHMARK_TEMPLATE2_DEFINE_F(MyTemplate2, TestB, int, double) +(benchmark::State &st) { for (auto _ : st) { } } diff --git a/examples/google_benchmark_cmake/main.cpp b/examples/google_benchmark_cmake/main.cpp index 035ae45..a81d16b 100644 --- a/examples/google_benchmark_cmake/main.cpp +++ b/examples/google_benchmark_cmake/main.cpp @@ -3,6 +3,7 @@ #include #include "fixture_bench.hpp" +#include "sleep_bench.hpp" #include "template_bench.hpp" template diff --git a/examples/google_benchmark_cmake/sleep_bench.hpp b/examples/google_benchmark_cmake/sleep_bench.hpp new file mode 100644 index 0000000..e91bc82 --- /dev/null +++ b/examples/google_benchmark_cmake/sleep_bench.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +#include +#include + +static void BM_sleep_1ns(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::nanoseconds(1)); + } +} +BENCHMARK(BM_sleep_1ns); + +static void BM_sleep_100ns(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::nanoseconds(100)); + } +} +BENCHMARK(BM_sleep_100ns); + +static void BM_sleep_1us(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::microseconds(1)); + } +} +BENCHMARK(BM_sleep_1us); + +static void BM_sleep_100us(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } +} +BENCHMARK(BM_sleep_100us); + +static void BM_sleep_1ms(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} +BENCHMARK(BM_sleep_1ms); + +static void BM_sleep_10ms(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} +BENCHMARK(BM_sleep_10ms); + +static void BM_sleep_50ms(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } +} +BENCHMARK(BM_sleep_50ms); + +static void BM_sleep_100ms(benchmark::State& state) { + for (auto _ : state) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} +BENCHMARK(BM_sleep_100ms); diff --git a/examples/google_benchmark_cmake/template_bench.hpp b/examples/google_benchmark_cmake/template_bench.hpp index 0625222..5d53236 100644 --- a/examples/google_benchmark_cmake/template_bench.hpp +++ b/examples/google_benchmark_cmake/template_bench.hpp @@ -2,11 +2,13 @@ #define TEMPLATE_BENCH_HPP #include + #include #include namespace test { -template void BM_Template(benchmark::State &state) { +template +void BM_Template(benchmark::State &state) { std::vector v; for (auto _ : state) { v.push_back(T()); @@ -14,12 +16,13 @@ template void BM_Template(benchmark::State &state) { } BENCHMARK_TEMPLATE(BM_Template, int); BENCHMARK_TEMPLATE(BM_Template, std::string); -} // namespace test +} // namespace test // // -template void BM_Template1(benchmark::State &state) { +template +void BM_Template1(benchmark::State &state) { T val = T(); for (auto _ : state) { benchmark::DoNotOptimize(val++); @@ -30,7 +33,8 @@ BENCHMARK_TEMPLATE1(BM_Template1, int); // // -template void BM_Template2(benchmark::State &state) { +template +void BM_Template2(benchmark::State &state) { T t = T(); U u = U(); for (auto _ : state) { diff --git a/google_benchmark/src/CMakeLists.txt b/google_benchmark/src/CMakeLists.txt index 389dd2e..4d4b622 100644 --- a/google_benchmark/src/CMakeLists.txt +++ b/google_benchmark/src/CMakeLists.txt @@ -92,7 +92,7 @@ set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc") set(pkg_config_main "${generated_dir}/${PROJECT_NAME}_main.pc") # TODO: Find a way to not expose codspeed headers to downstream users -set(targets_to_export benchmark benchmark_main codspeed) +set(targets_to_export benchmark benchmark_main codspeed instrument_hooks) set(targets_export_name "${PROJECT_NAME}Targets") set(namespace "${PROJECT_NAME}::")