Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ jobs:
strategy:
matrix:
include:
- codspeed-mode: "instrumentation"
- codspeed-mode: "simulation"
runner: "ubuntu-latest"
- codspeed-mode: "memory"
runner: "ubuntu-latest"
- codspeed-mode: "walltime"
runner: "codspeed-macro"
Expand Down Expand Up @@ -82,6 +84,12 @@ jobs:
cmake -DCODSPEED_MODE=${{ matrix.codspeed-mode }} -DCODSPEED_STRICT_WARNINGS=ON ..
make -j

- uses: dtolnay/rust-toolchain@stable
- name: Install the latest memtrack CLI
run: |
sudo apt-get update && sudo apt-get install -y autopoint bison flex
cargo install --git https://github.com/CodSpeedHQ/runner --branch cod-1715-support-non-libc-allocators memtrack

- name: Run the benchmarks
uses: CodSpeedHQ/action@main
if: matrix.codspeed-mode != 'off'
Expand All @@ -94,7 +102,9 @@ jobs:
strategy:
matrix:
include:
- codspeed-mode: "instrumentation"
- codspeed-mode: "simulation"
runner: "ubuntu-latest"
- codspeed-mode: "memory"
runner: "ubuntu-latest"
- codspeed-mode: "walltime"
runner: "codspeed-macro"
Expand All @@ -120,6 +130,12 @@ jobs:
run: |
bazel build //examples/google_benchmark_bazel:my_benchmark --@codspeed_core//:codspeed_mode=${{ matrix.codspeed-mode }} --@codspeed_core//:strict_warnings=on

- uses: dtolnay/rust-toolchain@stable
- name: Install the latest memtrack CLI
run: |
sudo apt-get update && sudo apt-get install -y autopoint bison flex
cargo install --git https://github.com/CodSpeedHQ/runner --branch cod-1715-support-non-libc-allocators memtrack

- name: Run the benchmarks
uses: CodSpeedHQ/action@main
if: matrix.codspeed-mode != 'off'
Expand Down
11 changes: 9 additions & 2 deletions core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ cc_library(
defines = [
"CODSPEED_VERSION=\\\"{}\\\"".format(CODSPEED_VERSION),
] + select({
":instrumentation_mode": ["CODSPEED_ENABLED", "CODSPEED_SIMULATION"],
":simulation_mode": ["CODSPEED_ENABLED", "CODSPEED_SIMULATION"],
":instrumentation_mode": ["CODSPEED_ENABLED", "CODSPEED_ANALYSIS"],
":simulation_mode": ["CODSPEED_ENABLED", "CODSPEED_ANALYSIS"],
":memory_mode": ["CODSPEED_ENABLED", "CODSPEED_ANALYSIS"],
":walltime_mode": ["CODSPEED_ENABLED", "CODSPEED_WALLTIME"],
"//conditions:default": [],
}),
Expand All @@ -98,6 +99,7 @@ string_flag(
"off",
"instrumentation",
"simulation",
"memory",
"walltime",
],
visibility = ["//visibility:public"],
Expand All @@ -113,6 +115,11 @@ config_setting(
flag_values = {":codspeed_mode": "simulation"},
)

config_setting(
name = "memory_mode",
flag_values = {":codspeed_mode": "memory"},
)

config_setting(
name = "walltime_mode",
flag_values = {":codspeed_mode": "walltime"},
Expand Down
6 changes: 3 additions & 3 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ target_compile_definitions(

message(STATUS "Using codspeed root directory: ${CODSPEED_ROOT_DIR}")

set(CODSPEED_MODE_ALLOWED_VALUES "off" "instrumentation" "simulation" "walltime")
set(CODSPEED_MODE_ALLOWED_VALUES "off" "instrumentation" "simulation" "memory" "walltime")
set(CODSPEED_MODE "off" CACHE STRING "Build mode for Codspeed")
set_property(
CACHE CODSPEED_MODE
Expand All @@ -166,10 +166,10 @@ if(NOT CODSPEED_MODE STREQUAL "off")
endif()

# Define a preprocessor macro based on the build mode
if(CODSPEED_MODE STREQUAL "instrumentation" OR CODSPEED_MODE STREQUAL "simulation")
if(CODSPEED_MODE STREQUAL "instrumentation" OR CODSPEED_MODE STREQUAL "simulation" OR CODSPEED_MODE STREQUAL "memory")
target_compile_definitions(
codspeed
PUBLIC -DCODSPEED_SIMULATION
PUBLIC -DCODSPEED_ANALYSIS
)
elseif(CODSPEED_MODE STREQUAL "walltime")
target_compile_definitions(codspeed PUBLIC -DCODSPEED_WALLTIME)
Expand Down
1 change: 1 addition & 0 deletions examples/google_benchmark_bazel/memory_bench.hpp
1 change: 1 addition & 0 deletions examples/google_benchmark_cmake/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "fibonacci_bench.hpp"
#include "fixture_bench.hpp"
#include "memory_bench.hpp"
#include "multithread_bench.hpp"
#include "pause_timing_bench.hpp"
#include "sleep_bench.hpp"
Expand Down
197 changes: 197 additions & 0 deletions examples/google_benchmark_cmake/memory_bench.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#pragma once

#include <benchmark/benchmark.h>

#include <string>
#include <vector>

// Run-length encoding: compress consecutive repeated characters
// Example: "aaabbbccc" -> "3a3b3c"
// NOTE: Intentionally inefficient - no pre-allocation to show multiple
// allocations
static std::string rle_encode(const std::string& input) {
if (input.empty()) return "";

std::string result; // No reserve - will trigger multiple reallocations

char current = input[0];
size_t count = 1;

for (size_t i = 1; i < input.size(); ++i) {
if (input[i] == current) {
count++;
} else {
// Create intermediate strings for each run
std::string count_str = std::to_string(count);
std::string run_encoded = count_str + current;
result += run_encoded; // Concatenation causes reallocations
current = input[i];
count = 1;
}
}

// Final run
std::string count_str = std::to_string(count);
std::string final_run = count_str + current;
result += final_run;

return result;
}

// Run-length decoding: decompress RLE encoded string
// Example: "3a3b3c" -> "aaabbbccc"
static std::string rle_decode(const std::string& input) {
std::string result;
size_t i = 0;

while (i < input.size()) {
// Parse the count
size_t count = 0;
while (i < input.size() && std::isdigit(input[i])) {
count = count * 10 + (input[i] - '0');
i++;
}

// Get the character
if (i < input.size()) {
char ch = input[i];
result.append(count, ch);
i++;
}
}

return result;
}

// Generate a string with patterns for RLE
static std::string generate_rle_input(size_t size, size_t run_length) {
std::string result;
result.reserve(size);

const std::string chars = "abcdefghijklmnopqrstuvwxyz";
size_t char_idx = 0;

while (result.size() < size) {
size_t count = std::min(run_length, size - result.size());
result.append(count, chars[char_idx % chars.size()]);
char_idx++;
}

return result;
}

// Benchmark: RLE encoding with small runs (high compression)
static void BM_RLE_Encode_SmallRuns(benchmark::State& state) {
const size_t input_size = state.range(0);
std::string input = generate_rle_input(input_size, 3);

for (auto _ : state) {
std::string encoded = rle_encode(input);
benchmark::DoNotOptimize(encoded);
benchmark::ClobberMemory();
}

state.SetBytesProcessed(state.iterations() * input_size);
}
BENCHMARK(BM_RLE_Encode_SmallRuns)
->Arg(100)
->Arg(1000)
->Arg(10000)
->Arg(100000);

// Benchmark: RLE encoding with large runs (low compression)
static void BM_RLE_Encode_LargeRuns(benchmark::State& state) {
const size_t input_size = state.range(0);
std::string input = generate_rle_input(input_size, 100);

for (auto _ : state) {
std::string encoded = rle_encode(input);
benchmark::DoNotOptimize(encoded);
benchmark::ClobberMemory();
}

state.SetBytesProcessed(state.iterations() * input_size);
}
BENCHMARK(BM_RLE_Encode_LargeRuns)
->Arg(100)
->Arg(1000)
->Arg(10000)
->Arg(100000);

// Benchmark: RLE decoding
static void BM_RLE_Decode(benchmark::State& state) {
const size_t input_size = state.range(0);
std::string input = generate_rle_input(input_size, 10);
std::string encoded = rle_encode(input);

for (auto _ : state) {
std::string decoded = rle_decode(encoded);
benchmark::DoNotOptimize(decoded);
benchmark::ClobberMemory();
}

state.SetBytesProcessed(state.iterations() * encoded.size());
}
BENCHMARK(BM_RLE_Decode)->Arg(100)->Arg(1000)->Arg(10000)->Arg(100000);

// Benchmark: Vector allocations (resizing pattern)
static void BM_Vector_PushBack(benchmark::State& state) {
const size_t count = state.range(0);

for (auto _ : state) {
std::vector<int> vec;
for (size_t i = 0; i < count; ++i) {
vec.push_back(static_cast<int>(i));
}
benchmark::DoNotOptimize(vec);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_Vector_PushBack)->Arg(10)->Arg(100)->Arg(1000)->Arg(10000);

// Benchmark: Vector allocations with reserve (optimized)
static void BM_Vector_Reserve(benchmark::State& state) {
const size_t count = state.range(0);

for (auto _ : state) {
std::vector<int> vec;
vec.reserve(count);
for (size_t i = 0; i < count; ++i) {
vec.push_back(static_cast<int>(i));
}
benchmark::DoNotOptimize(vec);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_Vector_Reserve)->Arg(10)->Arg(100)->Arg(1000)->Arg(10000);

// Benchmark: String concatenation (many allocations)
static void BM_String_Concatenation(benchmark::State& state) {
const size_t count = state.range(0);

for (auto _ : state) {
std::string result;
for (size_t i = 0; i < count; ++i) {
result += "x";
}
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_String_Concatenation)->Arg(10)->Arg(100)->Arg(1000)->Arg(10000);

// Benchmark: String concatenation with reserve (optimized)
static void BM_String_Reserve(benchmark::State& state) {
const size_t count = state.range(0);

for (auto _ : state) {
std::string result;
result.reserve(count);
for (size_t i = 0; i < count; ++i) {
result += "x";
}
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_String_Reserve)->Arg(10)->Arg(100)->Arg(1000)->Arg(10000);
12 changes: 6 additions & 6 deletions google_benchmark/include/benchmark/benchmark.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ class BENCHMARK_EXPORT BENCHMARK_INTERNAL_CACHELINE_ALIGNED State {

public:
const IterationCount max_iterations;
#if defined(CODSPEED_SIMULATION) || defined(CODSPEED_WALLTIME)
#if defined(CODSPEED_ANALYSIS) || defined(CODSPEED_WALLTIME)
codspeed::CodSpeed* codspeed_;
#endif
#ifdef CODSPEED_WALLTIME
Expand All @@ -973,7 +973,7 @@ class BENCHMARK_EXPORT BENCHMARK_INTERNAL_CACHELINE_ALIGNED State {
internal::ThreadTimer* timer, internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement,
ProfilerManager* profiler_manager
#if defined(CODSPEED_SIMULATION) || defined(CODSPEED_WALLTIME)
#if defined(CODSPEED_ANALYSIS) || defined(CODSPEED_WALLTIME)
,
codspeed::CodSpeed* codspeed = NULL
#endif
Expand Down Expand Up @@ -1071,12 +1071,12 @@ struct State::StateIterator {
if (BENCHMARK_BUILTIN_EXPECT(cached_ != 0, true)) {
return true;
}
#ifdef CODSPEED_SIMULATION
#ifdef CODSPEED_ANALYSIS
measurement_stop();
#endif
parent_->FinishKeepRunning();

#ifdef CODSPEED_SIMULATION
#ifdef CODSPEED_ANALYSIS
if (parent_->codspeed_ != NULL) {
parent_->codspeed_->end_benchmark();
}
Expand All @@ -1093,14 +1093,14 @@ inline BENCHMARK_ALWAYS_INLINE State::StateIterator State::begin() {
return StateIterator(this);
}
inline BENCHMARK_ALWAYS_INLINE State::StateIterator State::end() {
#ifdef CODSPEED_SIMULATION
#ifdef CODSPEED_ANALYSIS
if (this->codspeed_ != NULL) {
this->codspeed_->start_benchmark(name_);
}
#endif

StartKeepRunning();
#ifdef CODSPEED_SIMULATION
#ifdef CODSPEED_ANALYSIS
measurement_start();
#endif
return StateIterator();
Expand Down
6 changes: 3 additions & 3 deletions google_benchmark/src/benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,15 @@ State::State(std::string name, IterationCount max_iters,
internal::ThreadTimer* timer, internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement,
ProfilerManager* profiler_manager
#if defined(CODSPEED_SIMULATION) || defined(CODSPEED_WALLTIME)
#if defined(CODSPEED_ANALYSIS) || defined(CODSPEED_WALLTIME)
,
codspeed::CodSpeed* codspeed
#endif
)
: total_iterations_(0),
batch_leftover_(0),
max_iterations(max_iters),
#if defined(CODSPEED_SIMULATION) || defined(CODSPEED_WALLTIME)
#if defined(CODSPEED_ANALYSIS) || defined(CODSPEED_WALLTIME)
codspeed_(codspeed),
#endif
#ifdef CODSPEED_WALLTIME
Expand Down Expand Up @@ -462,7 +462,7 @@ void RunBenchmarks(const std::vector<BenchmarkInstance>& benchmarks,
#ifdef CODSPEED_ENABLED
auto& Err = display_reporter->GetErrorStream();
// Determine the width of the name field using a minimum width of 10.
#ifdef CODSPEED_SIMULATION
#ifdef CODSPEED_ANALYSIS
Err << "Codspeed mode: simulation" << "\n";
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message still says 'simulation' but this code path is now used for both simulation and memory modes under CODSPEED_ANALYSIS. The message should differentiate between these modes or be more generic.

Suggested change
Err << "Codspeed mode: simulation" << "\n";
Err << "Codspeed mode: analysis" << "\n";

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if printing analysis is the best UX, since we said it should be only the internal build mode. I guess we could forward the real measurement mode and then print that, while also enabling CODSPEED_WALLTIME/CODSPEED_ANALYSIS. wdyt?

#elif defined(CODSPEED_WALLTIME)
Err << "Codspeed mode: walltime" << "\n";
Expand Down
Loading
Loading