initial benchmarking

This commit is contained in:
cdemeyer-teachx
2025-08-14 17:13:05 +09:00
parent 5a2fe7c976
commit d209216d33
5 changed files with 352 additions and 30 deletions

56
benchmarks/CMakeLists.txt Normal file
View File

@@ -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
)

View File

@@ -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

View File

@@ -0,0 +1,81 @@
#include <benchmark/benchmark.h>
#include <pokemon_battle_sim.h>
// 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<int>(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<int>(state.range(0));
std::vector<PokemonSim::Pokemon> team1;
std::vector<PokemonSim::Pokemon> 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<size_t>(i)], team2[static_cast<size_t>(i)]);
}
// Reset all Pokemon health
for (int i = 0; i < numBattles; ++i) {
team1[static_cast<size_t>(i)].setHealth(100);
team2[static_cast<size_t>(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);

View File

@@ -0,0 +1,115 @@
#include <benchmark/benchmark.h>
#include <pokemon_battle_sim.h>
#include <vector>
#include <string>
#include <memory>
// 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<int>(state.range(0)));
int health = static_cast<int>(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<int>(state.range(0));
for (auto _ : state) {
std::vector<PokemonSim::Pokemon> 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<PokemonSim::Pokemon>("Pikachu", 100);
pokemon2 = std::make_unique<PokemonSim::Pokemon>("Charizard", 150);
}
void TearDown(const benchmark::State& /*state*/) override {
// Clean up after each benchmark iteration
pokemon1.reset();
pokemon2.reset();
}
protected:
std::unique_ptr<PokemonSim::Pokemon> pokemon1;
std::unique_ptr<PokemonSim::Pokemon> 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);

18
benchmarks/main.cpp Normal file
View File

@@ -0,0 +1,18 @@
#include <benchmark/benchmark.h>
// 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;
}