333209fa-0481-4a47-8399-e46cdc73ea81

This commit is contained in:
cdemeyer-teachx
2025-08-14 23:11:46 +00:00
parent 656aada919
commit 56c1baae2a
8 changed files with 1757 additions and 12 deletions

View File

@@ -0,0 +1,449 @@
#include <benchmark/benchmark.h>
#include "types.h"
#include <vector>
#include <random>
#include <algorithm>
namespace PokemonSim {
// Benchmark fixture for type system
class TypeSystemBenchmark : public benchmark::Fixture {
public:
void SetUp(const benchmark::State& /*state*/) override {
// Set up common test data
SetupTestData();
}
void TearDown(const benchmark::State& /*state*/) override {
// Clean up
}
void SetupTestData() {
// Create a variety of Pokemon types for testing
singleTypePokemon = {
PokemonTypes(Type::FIRE),
PokemonTypes(Type::WATER),
PokemonTypes(Type::GRASS),
PokemonTypes(Type::ELECTRIC),
PokemonTypes(Type::NORMAL),
PokemonTypes(Type::FIGHTING),
PokemonTypes(Type::POISON),
PokemonTypes(Type::GROUND),
PokemonTypes(Type::FLYING),
PokemonTypes(Type::PSYCHIC)
};
dualTypePokemon = {
PokemonTypes(Type::FIRE, Type::FLYING), // Charizard
PokemonTypes(Type::WATER, Type::POISON), // Tentacruel
PokemonTypes(Type::GRASS, Type::POISON), // Venusaur
PokemonTypes(Type::ELECTRIC, Type::STEEL), // Magnezone
PokemonTypes(Type::NORMAL, Type::FLYING), // Pidgeot
PokemonTypes(Type::FIGHTING, Type::DARK), // Lucario
PokemonTypes(Type::POISON, Type::DARK), // Drapion
PokemonTypes(Type::GROUND, Type::ROCK), // Rhyperior
PokemonTypes(Type::FLYING, Type::DRAGON), // Dragonite
PokemonTypes(Type::PSYCHIC, Type::FAIRY) // Gardevoir
};
// Create attack types for testing
attackTypes = {
Type::NORMAL, Type::FIRE, Type::WATER, Type::ELECTRIC, Type::GRASS,
Type::ICE, Type::FIGHTING, Type::POISON, Type::GROUND, Type::FLYING,
Type::PSYCHIC, Type::BUG, Type::ROCK, Type::GHOST, Type::DRAGON,
Type::DARK, Type::STEEL, Type::FAIRY
};
}
protected:
std::vector<PokemonTypes> singleTypePokemon;
std::vector<PokemonTypes> dualTypePokemon;
std::vector<Type> attackTypes;
};
// Benchmark type string conversion performance
BENCHMARK_F(TypeSystemBenchmark, BM_StringToTypeConversion)(benchmark::State& state) {
std::vector<std::string> typeStrings = {"fire", "water", "grass", "electric", "normal", "fighting", "poison"};
for (auto _ : state) {
for (const auto& typeStr : typeStrings) {
auto result = TypeUtils::stringToType(typeStr);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(TypeSystemBenchmark, BM_TypeToStringConversion)(benchmark::State& state) {
for (auto _ : state) {
for (size_t i = 0; i < attackTypes.size(); ++i) {
auto result = TypeUtils::typeToString(attackTypes[i % attackTypes.size()]);
benchmark::DoNotOptimize(result);
}
}
}
// Benchmark type effectiveness lookup performance
BENCHMARK_F(TypeSystemBenchmark, BM_TypeEffectivenessLookup)(benchmark::State& state) {
for (auto _ : state) {
for (size_t i = 0; i < attackTypes.size(); ++i) {
for (size_t j = 0; j < attackTypes.size(); ++j) {
auto result = TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(
attackTypes[i % attackTypes.size()],
attackTypes[j % attackTypes.size()]
);
benchmark::DoNotOptimize(result);
}
}
}
}
BENCHMARK_F(TypeSystemBenchmark, BM_TypeEffectivenessLookupGen8)(benchmark::State& state) {
for (auto _ : state) {
for (size_t i = 0; i < attackTypes.size(); ++i) {
for (size_t j = 0; j < attackTypes.size(); ++j) {
auto result = TypeUtils::getTypeEffectiveness<Generation::GENERATION_8>(
attackTypes[i % attackTypes.size()],
attackTypes[j % attackTypes.size()]
);
benchmark::DoNotOptimize(result);
}
}
}
}
// Benchmark damage multiplier calculation for single-type Pokemon
BENCHMARK_F(TypeSystemBenchmark, BM_DamageMultiplierSingleType)(benchmark::State& state) {
for (auto _ : state) {
for (const auto& pokemon : singleTypePokemon) {
for (const auto& attackType : attackTypes) {
auto result = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(attackType, pokemon);
benchmark::DoNotOptimize(result);
}
}
}
}
// Benchmark damage multiplier calculation for dual-type Pokemon
BENCHMARK_F(TypeSystemBenchmark, BM_DamageMultiplierDualType)(benchmark::State& state) {
for (auto _ : state) {
for (const auto& pokemon : dualTypePokemon) {
for (const auto& attackType : attackTypes) {
auto result = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(attackType, pokemon);
benchmark::DoNotOptimize(result);
}
}
}
}
// Benchmark the high-level damage calculation function
BENCHMARK_F(TypeSystemBenchmark, BM_CalculateDamage)(benchmark::State& state) {
const uint32_t baseDamage = 100;
for (auto _ : state) {
for (const auto& pokemon : singleTypePokemon) {
for (const auto& attackType : attackTypes) {
auto result = calculateDamage<Generation::GENERATION_1>(baseDamage, attackType, pokemon);
benchmark::DoNotOptimize(result);
}
}
}
}
BENCHMARK_F(TypeSystemBenchmark, BM_CalculateDamageDualType)(benchmark::State& state) {
const uint32_t baseDamage = 100;
for (auto _ : state) {
for (const auto& pokemon : dualTypePokemon) {
for (const auto& attackType : attackTypes) {
auto result = calculateDamage<Generation::GENERATION_1>(baseDamage, attackType, pokemon);
benchmark::DoNotOptimize(result);
}
}
}
}
// Benchmark different generations
BENCHMARK_F(TypeSystemBenchmark, BM_Generation1DamageCalc)(benchmark::State& state) {
const uint32_t baseDamage = 100;
PokemonTypes pokemon(Type::FIRE, Type::FLYING);
for (auto _ : state) {
for (const auto& attackType : attackTypes) {
auto result = calculateDamage<Generation::GENERATION_1>(baseDamage, attackType, pokemon);
benchmark::DoNotOptimize(result);
}
}
}
BENCHMARK_F(TypeSystemBenchmark, BM_Generation8DamageCalc)(benchmark::State& state) {
const uint32_t baseDamage = 100;
PokemonTypes pokemon(Type::FIRE, Type::FLYING);
for (auto _ : state) {
for (const auto& attackType : attackTypes) {
auto result = calculateDamage<Generation::GENERATION_8>(baseDamage, attackType, pokemon);
benchmark::DoNotOptimize(result);
}
}
}
// Benchmark PokemonTypes operations
BENCHMARK_F(TypeSystemBenchmark, BM_PokemonTypesOperations)(benchmark::State& state) {
for (auto _ : state) {
PokemonTypes pokemon(Type::FIRE);
// Test setting types
pokemon.setPrimary(Type::WATER);
pokemon.setSecondary(Type::FLYING);
// Test getting types
auto primary = pokemon.getPrimary();
auto secondary = pokemon.getSecondary();
auto hasSecondary = pokemon.hasSecondary();
benchmark::DoNotOptimize(primary);
benchmark::DoNotOptimize(secondary);
benchmark::DoNotOptimize(hasSecondary);
}
}
// Benchmark batch processing (simulating real game scenarios)
BENCHMARK_F(TypeSystemBenchmark, BM_BatchDamageCalculation)(benchmark::State& state) {
const size_t batchSize = state.range(0);
const uint32_t baseDamage = 100;
// Prepare batch data
std::vector<std::tuple<Type, PokemonTypes>> batch;
batch.reserve(batchSize);
for (size_t i = 0; i < batchSize; ++i) {
Type attackType = attackTypes[i % attackTypes.size()];
PokemonTypes defendTypes = (i % 2 == 0) ?
singleTypePokemon[i % singleTypePokemon.size()] :
dualTypePokemon[i % dualTypePokemon.size()];
batch.emplace_back(attackType, defendTypes);
}
for (auto _ : state) {
uint32_t totalDamage = 0;
for (const auto& [attackType, defendTypes] : batch) {
uint32_t damage = calculateDamage<Generation::GENERATION_1>(baseDamage, attackType, defendTypes);
totalDamage += damage;
}
benchmark::DoNotOptimize(totalDamage);
}
}
BENCHMARK_REGISTER_F(TypeSystemBenchmark, BM_BatchDamageCalculation)
->Arg(10) // Small batch
->Arg(100) // Medium batch
->Arg(1000) // Large batch
->Unit(benchmark::kMicrosecond);
// Memory access pattern benchmark
BENCHMARK_F(TypeSystemBenchmark, BM_TypeChartMemoryAccess)(benchmark::State& state) {
// Test memory access patterns for type chart lookups
const auto& chart = TypeChartTraits<Generation::GENERATION_1>::getTypeChart();
for (auto _ : state) {
// Sequential access pattern
for (size_t i = 0; i < chart.size(); ++i) {
auto multiplier = chart[i];
benchmark::DoNotOptimize(multiplier);
}
}
}
BENCHMARK_F(TypeSystemBenchmark, BM_TypeChartRandomAccess)(benchmark::State& state) {
const auto& chart = TypeChartTraits<Generation::GENERATION_1>::getTypeChart();
const size_t chartSize = chart.size();
// Generate random indices
std::vector<size_t> randomIndices;
randomIndices.reserve(1000);
std::mt19937 gen(42); // Fixed seed for reproducibility
std::uniform_int_distribution<size_t> dist(0, chartSize - 1);
for (size_t i = 0; i < 1000; ++i) {
randomIndices.push_back(dist(gen));
}
for (auto _ : state) {
// Random access pattern
for (size_t idx : randomIndices) {
auto multiplier = chart[idx];
benchmark::DoNotOptimize(multiplier);
}
}
}
// Cache-friendly vs cache-unfriendly access patterns
BENCHMARK_F(TypeSystemBenchmark, BM_CacheFriendlyLookups)(benchmark::State& state) {
// Cache-friendly: sequential type lookups
for (auto _ : state) {
for (size_t i = 0; i < attackTypes.size(); ++i) {
for (size_t j = 0; j < attackTypes.size(); ++j) {
// Predictable access pattern
size_t attackIdx = i;
size_t defendIdx = j;
size_t index = attackIdx * static_cast<size_t>(Type::TYPE_COUNT) + defendIdx;
auto result = TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(
attackTypes[attackIdx % attackTypes.size()],
attackTypes[defendIdx % attackTypes.size()]
);
benchmark::DoNotOptimize(result);
}
}
}
}
BENCHMARK_F(TypeSystemBenchmark, BM_CacheUnfriendlyLookups)(benchmark::State& state) {
// Cache-unfriendly: random type lookups
std::vector<std::pair<size_t, size_t>> randomPairs;
randomPairs.reserve(1000);
std::mt19937 gen(42);
std::uniform_int_distribution<size_t> dist(0, attackTypes.size() - 1);
for (size_t i = 0; i < 1000; ++i) {
randomPairs.emplace_back(dist(gen), dist(gen));
}
for (auto _ : state) {
for (const auto& [attackIdx, defendIdx] : randomPairs) {
auto result = TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(
attackTypes[attackIdx],
attackTypes[defendIdx]
);
benchmark::DoNotOptimize(result);
}
}
}
// Individual benchmarks for standalone functions
static void BM_TypeEffectivenessFunction(benchmark::State& state) {
Type attackType = Type::WATER;
Type defendType = Type::FIRE;
for (auto _ : state) {
auto result = TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(attackType, defendType);
benchmark::DoNotOptimize(result);
}
}
static void BM_DamageMultiplierFunction(benchmark::State& state) {
Type attackType = Type::WATER;
PokemonTypes defender(Type::FIRE, Type::FLYING);
for (auto _ : state) {
auto result = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(attackType, defender);
benchmark::DoNotOptimize(result);
}
}
static void BM_CalculateDamageFunction(benchmark::State& state) {
uint32_t baseDamage = 100;
Type attackType = Type::WATER;
PokemonTypes defender(Type::FIRE, Type::FLYING);
for (auto _ : state) {
auto result = calculateDamage<Generation::GENERATION_1>(baseDamage, attackType, defender);
benchmark::DoNotOptimize(result);
}
}
BENCHMARK(BM_TypeEffectivenessFunction)
->Unit(benchmark::kNanosecond)
->Iterations(1000000);
BENCHMARK(BM_DamageMultiplierFunction)
->Unit(benchmark::kNanosecond)
->Iterations(1000000);
BENCHMARK(BM_CalculateDamageFunction)
->Unit(benchmark::kNanosecond)
->Iterations(1000000);
// Complex scenario benchmark (simulating a full battle turn)
static void BM_BattleTurnSimulation(benchmark::State& state) {
// Simulate a battle turn with multiple Pokemon and moves
const size_t numAttackers = 6; // Full team
const size_t numDefenders = 6; // Full opposing team
const size_t movesPerTurn = 4; // Multiple moves per Pokemon
struct BattlePokemon {
PokemonTypes types;
std::vector<Type> moves;
};
std::vector<BattlePokemon> attackers;
std::vector<BattlePokemon> defenders;
// Setup realistic Pokemon teams
std::vector<std::pair<Type, Type>> attackerTypes = {
{Type::FIRE, Type::FLYING}, // Charizard
{Type::WATER, Type::NONE}, // Blastoise
{Type::GRASS, Type::POISON}, // Venusaur
{Type::ELECTRIC, Type::NONE}, // Raichu
{Type::FIGHTING, Type::NONE}, // Machamp
{Type::NORMAL, Type::FLYING} // Pidgeot
};
std::vector<std::pair<Type, Type>> defenderTypes = {
{Type::WATER, Type::POISON}, // Tentacruel
{Type::ROCK, Type::GROUND}, // Rhyperior
{Type::ELECTRIC, Type::STEEL}, // Magnezone
{Type::PSYCHIC, Type::NONE}, // Alakazam
{Type::DARK, Type::NONE}, // Umbreon
{Type::FAIRY, Type::NONE} // Sylveon
};
// Setup moves for each Pokemon
std::vector<std::vector<Type>> moveSets = {
{Type::FIRE, Type::FLYING, Type::DRAGON, Type::NORMAL}, // Charizard moves
{Type::WATER, Type::NORMAL, Type::ICE, Type::FIGHTING}, // Blastoise moves
{Type::GRASS, Type::POISON, Type::GROUND, Type::NORMAL}, // Venusaur moves
{Type::ELECTRIC, Type::NORMAL, Type::PSYCHIC, Type::DARK}, // Raichu moves
{Type::FIGHTING, Type::NORMAL, Type::ROCK, Type::GROUND}, // Machamp moves
{Type::FLYING, Type::NORMAL, Type::GRASS, Type::ICE} // Pidgeot moves
};
for (size_t i = 0; i < numAttackers; ++i) {
attackers.push_back({
PokemonTypes(attackerTypes[i].first, attackerTypes[i].second),
moveSets[i]
});
}
for (size_t i = 0; i < numDefenders; ++i) {
defenders.push_back({
PokemonTypes(defenderTypes[i].first, defenderTypes[i].second),
moveSets[i % moveSets.size()] // Reuse movesets for simplicity
});
}
for (auto _ : state) {
uint32_t totalDamage = 0;
// Simulate one full battle turn
for (const auto& attacker : attackers) {
for (const auto& defender : defenders) {
for (const auto& move : attacker.moves) {
// Calculate damage for each move against each defender
uint32_t damage = calculateDamage<Generation::GENERATION_1>(100, move, defender.types);
totalDamage += damage;
}
}
}
benchmark::DoNotOptimize(totalDamage);
}
}
BENCHMARK(BM_BattleTurnSimulation)
->Unit(benchmark::kMicrosecond)
->Iterations(100);
} // namespace PokemonSim