# Shardok Performance Optimization Plan

## Current Status
PostActionUnchecked reduced from 45.4% to 39.3% of total runtime after shared_ptr optimizations.

## ✅ Completed Optimizations

### 1. APDCache Thread-Local Caching
- **Problem**: PreCachedAPDs constructor taking 18.5% of processing time
- **Solution**: Moved thread-local caching into APDCache API using existing FullCacheKey infrastructure
- **Implementation**: Hybrid API with both shared_ptr and raw pointer access, migrated 21+ call sites
- **Result**: Successfully eliminated shared_ptr overhead in AI calculations

### 2. SharedPtr Reference Counting Fix  
- **Problem**: Atomic reference counting overhead in ShardokAction::Execute (28.4% of total runtime)
- **Solution**: Changed RandomGenerator parameter from `std::shared_ptr<RandomGenerator>` to `const std::shared_ptr<RandomGenerator>&`
- **Implementation**: Updated 49+ override sites across all command and action classes
- **Result**: Reduced PostActionUnchecked from 45.4% to 39.3% of runtime

## ❌ Failed Attempts

### 1. ToByteString() Caching
- **Problem**: Suspected expensive game state serialization calls
- **Solution**: Added hash-based caching to avoid repeated ToByteString() calls
- **Result**: No measurable performance improvement (discarded)

## 📋 Next Steps (Priority Order)

### 1. Optimize Occupant() with Array-based Indexing (HIGH PRIORITY)
- **Problem**: `Occupant()` function iterates through ALL units (O(n)) to find unit at specific coordinates
- **Solution**: Replace with O(1) array lookup indexed by `row * columnCount + column`
- **Implementation**: 
  - Simple array storing UnitId (or INVALID_UNIT_ID) at each map position
  - Update index when units move/spawn/die
  - Use in GameStateW wrapper with lazy initialization
- **Rationale**: Clear algorithmic improvement, frequently called function
- **Expected Impact**: Unknown but potentially significant

### 2. Profile Next Bottleneck (HIGH PRIORITY)
- **Goal**: After Occupant() optimization, re-profile to identify next hotspot
- **Focus**: PostActionUnchecked still 39.3% - drill deeper into remaining time consumption
- **Approach**: Look for unexpected bottlenecks like the shared_ptr reference counting we discovered
- **Rationale**: Profiling has revealed surprising performance issues

### 3. Defer UpdateGameStatusAction (MEDIUM PRIORITY)
- **Problem**: Victory conditions checked after every action
- **Solution**: Batch victory condition checks to end of turn or specific triggers
- **Expected Impact**: Reduce redundant computation overhead

### 4. Object Pooling (MEDIUM PRIORITY)
- **Problem**: Frequent allocation/deallocation of ActionResult and other objects
- **Solution**: Implement object pools for frequently created objects
- **Focus**: ActionResult objects, other high-frequency allocations
- **Expected Impact**: Reduce memory allocation overhead

### 5. Lazy Modifier Hash Calculation (LOW PRIORITY)
- **Problem**: Hash calculations performed unnecessarily
- **Solution**: Compute hashes only when needed, cache between modifications
- **Expected Impact**: Minor optimization for specific scenarios

## Key Insights

1. **Profiling Reveals Surprises**: Both major optimizations (APDCache and shared_ptr) were discovered through profiling rather than intuition
2. **Atomic Operations Are Expensive**: Shared_ptr reference counting showed up as significant assembly-level overhead
3. **Algorithmic Improvements Matter**: O(n) → O(1) optimizations like the proposed Occupant() fix are worth pursuing
4. **Measurement is Critical**: ToByteString() caching seemed logical but provided no benefit
5. **Incremental Progress**: Each optimization reveals the next bottleneck, requiring continuous profiling

## Implementation Notes

- Always profile before and after changes to measure actual impact
- Be prepared to discard optimizations that don't provide measurable benefit
- Focus on algorithmic improvements and unexpected bottlenecks revealed by profiling
- Continue systematic analysis of PostActionUnchecked hotspots