From d209216d3331676a18f01900fd2fb9a0cdf3f513 Mon Sep 17 00:00:00 2001 From: cdemeyer-teachx Date: Thu, 14 Aug 2025 17:13:05 +0900 Subject: [PATCH] initial benchmarking --- benchmarks/CMakeLists.txt | 56 ++++++++++ benchmarks/README.md | 112 ++++++++++++++----- benchmarks/core/battle_simulation_bench.cpp | 81 ++++++++++++++ benchmarks/core/pokemon_creation_bench.cpp | 115 ++++++++++++++++++++ benchmarks/main.cpp | 18 +++ 5 files changed, 352 insertions(+), 30 deletions(-) create mode 100644 benchmarks/CMakeLists.txt create mode 100644 benchmarks/core/battle_simulation_bench.cpp create mode 100644 benchmarks/core/pokemon_creation_bench.cpp create mode 100644 benchmarks/main.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt new file mode 100644 index 0000000..46d9b37 --- /dev/null +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,56 @@ +# Benchmarks CMakeLists.txt +include(FetchContent) + +# Download and configure Google Benchmark +FetchContent_Declare( + googlebenchmark + URL https://github.com/google/benchmark/archive/v1.8.3.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE +) + +# Prevent overriding the parent project's compiler/linker settings +set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Suppressing benchmark's tests" FORCE) +set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "Suppressing benchmark's gtest tests" FORCE) + +# Suppress warnings in Google Benchmark build +set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "Disable -Werror in benchmark build" FORCE) + +# Make Google Benchmark available +FetchContent_MakeAvailable(googlebenchmark) + +# Get the benchmark target and suppress warnings +if(TARGET benchmark) + target_compile_options(benchmark PRIVATE + -Wno-conversion + -Wno-sign-conversion + ) +endif() + +if(TARGET benchmark_main) + target_compile_options(benchmark_main PRIVATE + -Wno-conversion + -Wno-sign-conversion + ) +endif() + +# Create benchmark executable +add_executable(benchmarks + main.cpp + core/battle_simulation_bench.cpp + core/pokemon_creation_bench.cpp +) + +# Link with Google Benchmark and our library +target_link_libraries(benchmarks + PRIVATE + benchmark::benchmark + benchmark::benchmark_main + PokemonSim::pokemon_battle_sim +) + +# Include directories +target_include_directories(benchmarks + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include +) diff --git a/benchmarks/README.md b/benchmarks/README.md index 239e47e..acb39d7 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -1,40 +1,92 @@ # Benchmarks Directory (`benchmarks/`) -This directory contains performance benchmarks and profiling tools for the Pokemon battle simulator. +This directory contains performance benchmarks and profiling tools for the Pokemon battle simulator using Google Benchmark. -## Planned Structure +## Quick Start -``` -benchmarks/ -├── core/ # Core system benchmarks -│ ├── battle_speed.cpp # Battle simulation speed -│ ├── pokemon_creation.cpp # Pokemon instantiation -│ ├── move_calculation.cpp # Move damage calculations -│ └── type_lookup.cpp # Type effectiveness lookups -├── data/ # Data loading benchmarks -│ ├── json_parsing.cpp # JSON data parsing speed -│ └── data_caching.cpp # Cache performance -├── ai/ # AI performance benchmarks -│ ├── ai_decision_time.cpp # AI move selection speed -│ └── minimax_depth.cpp # Minimax algorithm scaling -├── memory/ # Memory usage benchmarks -│ ├── memory_usage.cpp # Memory footprint analysis -│ └── allocation_speed.cpp # Dynamic allocation performance -├── scenarios/ # Real-world scenario benchmarks -│ ├── tournament.cpp # Tournament simulation -│ └── monte_carlo.cpp # Monte Carlo battle analysis -├── results/ # Benchmark results and reports -│ ├── baseline/ # Baseline performance data -│ └── reports/ # Generated performance reports -└── CMakeLists.txt # Benchmark build configuration +To run the benchmarks: + +```bash +# Build with benchmarks enabled +cmake -DBUILD_BENCHMARKS=ON .. +make -j$(nproc) + +# Run benchmarks +./benchmarks/benchmarks + +# Run specific benchmark +./benchmarks/benchmarks --benchmark_filter=BM_BattleSimulation + +# Save results to JSON +./benchmarks/benchmarks --benchmark_out=results.json --benchmark_out_format=json ``` -## Benchmarking Framework +## Benchmark Examples -- **Google Benchmark**: Primary benchmarking library -- **Automated Regression Detection**: Performance alerts on slowdowns -- **Multiple Metrics**: Time, memory, CPU usage, cache misses -- **Statistical Analysis**: Multiple runs with confidence intervals +### Basic Benchmark +```cpp +static void BM_SimpleFunction(benchmark::State& state) { + for (auto _ : state) { + // Code to benchmark + function_to_test(); + } +} +BENCHMARK(BM_SimpleFunction); +``` + +### Parameterized Benchmark +```cpp +static void BM_Parameterized(benchmark::State& state) { + int size = state.range(0); + for (auto _ : state) { + // Code using size parameter + } +} +BENCHMARK(BM_Parameterized)->Arg(100)->Arg(1000)->Arg(10000); +``` + +### Fixture-based Benchmark +```cpp +class MyFixture : public benchmark::Fixture { +public: + void SetUp(const benchmark::State& state) override { + // Setup code + } + void TearDown(const benchmark::State& state) override { + // Cleanup code + } +}; + +BENCHMARK_F(MyFixture, BM_TestWithFixture)(benchmark::State& state) { + for (auto _ : state) { + // Test code + } +} +``` + +## Current Benchmarks + +### Core Benchmarks (`core/`) +- **Battle Simulation**: Measures battle simulation performance +- **Pokemon Creation**: Tests Pokemon instantiation speed +- **Bulk Operations**: Performance with multiple Pokemon +- **Health Operations**: Getter/setter performance + +## Benchmark Output + +The benchmarks provide detailed timing information including: +- **Mean**: Average execution time +- **Median**: Middle value of execution times +- **StdDev**: Standard deviation of results +- **Iterations**: Number of times the benchmark was run + +## Performance Optimization Tips + +1. **Use `benchmark::DoNotOptimize()`** to prevent compiler optimization +2. **Use `benchmark::ClobberMemory()`** for memory benchmarks +3. **Set appropriate iteration counts** for consistent measurements +4. **Use fixtures** for complex setup/teardown scenarios +5. **Run multiple repetitions** for statistical significance ## Key Performance Targets diff --git a/benchmarks/core/battle_simulation_bench.cpp b/benchmarks/core/battle_simulation_bench.cpp new file mode 100644 index 0000000..3eaed4e --- /dev/null +++ b/benchmarks/core/battle_simulation_bench.cpp @@ -0,0 +1,81 @@ +#include +#include + +// Benchmark for battle simulation performance +static void BM_BattleSimulation(benchmark::State& state) { + // Set up Pokemon for the benchmark + PokemonSim::Pokemon pikachu("Pikachu", 100); + PokemonSim::Pokemon charizard("Charizard", 150); + + // Run the benchmark + for (auto _ : state) { + // Simulate a battle between the two Pokemon + PokemonSim::simulateBattle(pikachu, charizard); + + // Reset health for next iteration + pikachu.setHealth(100); + charizard.setHealth(150); + } +} + +// Register the benchmark with different configurations +BENCHMARK(BM_BattleSimulation) + ->Unit(benchmark::kMicrosecond) // Measure in microseconds + ->Iterations(1000) // Run 1000 iterations + ->Repetitions(3); // Repeat the benchmark 3 times for statistical analysis + +// Benchmark with varying Pokemon health values +static void BM_BattleSimulationWithHealth(benchmark::State& state) { + int health = static_cast(state.range(0)); + + PokemonSim::Pokemon pokemon1("Pokemon1", health); + PokemonSim::Pokemon pokemon2("Pokemon2", health); + + for (auto _ : state) { + PokemonSim::simulateBattle(pokemon1, pokemon2); + + // Reset health + pokemon1.setHealth(health); + pokemon2.setHealth(health); + } +} + +// Register benchmark with different health values +BENCHMARK(BM_BattleSimulationWithHealth) + ->Arg(50) // Test with 50 HP + ->Arg(100) // Test with 100 HP + ->Arg(200) // Test with 200 HP + ->Unit(benchmark::kMicrosecond); + +// Benchmark for multiple battle simulations +static void BM_MultipleBattles(benchmark::State& state) { + int numBattles = static_cast(state.range(0)); + + std::vector team1; + std::vector team2; + + // Create teams of Pokemon + for (int i = 0; i < numBattles; ++i) { + team1.emplace_back("Pikachu" + std::to_string(i), 100); + team2.emplace_back("Charizard" + std::to_string(i), 150); + } + + for (auto _ : state) { + // Simulate multiple battles + for (int i = 0; i < numBattles; ++i) { + PokemonSim::simulateBattle(team1[static_cast(i)], team2[static_cast(i)]); + } + + // Reset all Pokemon health + for (int i = 0; i < numBattles; ++i) { + team1[static_cast(i)].setHealth(100); + team2[static_cast(i)].setHealth(150); + } + } +} + +BENCHMARK(BM_MultipleBattles) + ->Arg(10) // Test with 10 battles + ->Arg(50) // Test with 50 battles + ->Arg(100) // Test with 100 battles + ->Unit(benchmark::kMillisecond); diff --git a/benchmarks/core/pokemon_creation_bench.cpp b/benchmarks/core/pokemon_creation_bench.cpp new file mode 100644 index 0000000..f8db5d7 --- /dev/null +++ b/benchmarks/core/pokemon_creation_bench.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +// Benchmark for Pokemon creation performance +static void BM_PokemonCreation(benchmark::State& state) { + for (auto _ : state) { + // Create a Pokemon - this is what we're measuring + PokemonSim::Pokemon pokemon("Pikachu", 100); + + // Prevent compiler optimization from eliminating the Pokemon + benchmark::DoNotOptimize(pokemon); + } +} + +BENCHMARK(BM_PokemonCreation) + ->Unit(benchmark::kNanosecond) + ->Iterations(10000); + +// Benchmark for Pokemon creation with different names and health values +static void BM_PokemonCreationParameterized(benchmark::State& state) { + std::string name = "Pokemon" + std::to_string(static_cast(state.range(0))); + int health = static_cast(state.range(1)); + + for (auto _ : state) { + PokemonSim::Pokemon pokemon(name, health); + benchmark::DoNotOptimize(pokemon); + } +} + +BENCHMARK(BM_PokemonCreationParameterized) + ->Args({1, 50}) // Pokemon1, 50 HP + ->Args({2, 100}) // Pokemon2, 100 HP + ->Args({3, 200}) // Pokemon3, 200 HP + ->Unit(benchmark::kNanosecond); + +// Benchmark for bulk Pokemon creation +static void BM_BulkPokemonCreation(benchmark::State& state) { + int numPokemon = static_cast(state.range(0)); + + for (auto _ : state) { + std::vector pokemonList; + + // Create multiple Pokemon + for (int i = 0; i < numPokemon; ++i) { + pokemonList.emplace_back("Pokemon" + std::to_string(i), 100 + i * 10); + } + + benchmark::DoNotOptimize(pokemonList); + benchmark::ClobberMemory(); // Ensure memory operations are not optimized away + } +} + +BENCHMARK(BM_BulkPokemonCreation) + ->Arg(10) // Create 10 Pokemon + ->Arg(100) // Create 100 Pokemon + ->Arg(1000) // Create 1000 Pokemon + ->Unit(benchmark::kMicrosecond); + +// Benchmark for Pokemon health modifications +static void BM_PokemonHealthOperations(benchmark::State& state) { + PokemonSim::Pokemon pokemon("TestPokemon", 100); + + for (auto _ : state) { + // Perform various health operations + pokemon.setHealth(50); + int health = pokemon.getHealth(); + pokemon.setHealth(health + 25); + std::string name = pokemon.getName(); + + benchmark::DoNotOptimize(health); + benchmark::DoNotOptimize(name); + } +} + +BENCHMARK(BM_PokemonHealthOperations) + ->Unit(benchmark::kNanosecond) + ->Iterations(100000); + +// Fixture for benchmarks that need setup/teardown +class PokemonFixture : public benchmark::Fixture { +public: + void SetUp(const benchmark::State& /*state*/) override { + // Set up Pokemon for each benchmark iteration + pokemon1 = std::make_unique("Pikachu", 100); + pokemon2 = std::make_unique("Charizard", 150); + } + + void TearDown(const benchmark::State& /*state*/) override { + // Clean up after each benchmark iteration + pokemon1.reset(); + pokemon2.reset(); + } + +protected: + std::unique_ptr pokemon1; + std::unique_ptr pokemon2; +}; + +// Benchmark using fixture +BENCHMARK_F(PokemonFixture, BM_BattleWithFixture)(benchmark::State& state) { + for (auto _ : state) { + PokemonSim::simulateBattle(*pokemon1, *pokemon2); + + // Reset health + pokemon1->setHealth(100); + pokemon2->setHealth(150); + } +} + +BENCHMARK_REGISTER_F(PokemonFixture, BM_BattleWithFixture) + ->Unit(benchmark::kMicrosecond) + ->Iterations(1000); diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp new file mode 100644 index 0000000..7f26961 --- /dev/null +++ b/benchmarks/main.cpp @@ -0,0 +1,18 @@ +#include + +// Main function for Google Benchmark +// This is automatically provided by benchmark::benchmark_main +// but we include it for clarity and potential custom initialization + +int main(int argc, char** argv) { + // Initialize benchmark + ::benchmark::Initialize(&argc, argv); + + // Set any global benchmark configuration here + // For example, you can set the minimum time per benchmark, etc. + + // Run the benchmarks + ::benchmark::RunSpecifiedBenchmarks(); + + return 0; +}