Skip to content

Optimize jbwapi - #110

Draft
JasperGeurtz wants to merge 7 commits into
developfrom
perf-optimizations
Draft

JasperGeurtz wants to merge 7 commits into
developfrom
perf-optimizations

Conversation

@JasperGeurtz

@JasperGeurtz JasperGeurtz commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR reduces per-frame CPU overhead, shared-memory ByteBuffer reads, and heap/GC allocation pressure

  1. Lazy Per-Frame UnitListCache (Game, Player):

    • Replaces eager stream filtering on Game.getMinerals(), Game.getGeysers(), Game.getNeutralUnits(), Player.getUnits(), and internal selfPylons (Game.hasPower) with lazy per-frame UnitListCache instances that compute at most once per frame on demand and return the cached unmodifiable list on subsequent calls.
  2. Lazy Write-Only Latency Compensation (UnitSelf, PlayerSelf):

    • Unit.self (37 Cache objects per unit) and Player.self (520 Cache objects per player) are now allocated lazily only when CommandTemp mutates latency-compensated state, rather than on the first getter read or unconditionally on construction.
    • Uncommanded, neutral, and enemy units/players no longer pay the latency-compensation memory and initialization cost.
  3. Allocation-Free Unit.getDistance & Stationary Unit.updatePosition (Point, Unit):

    • Unit.getDistance(Position) and Unit.getDistance(Unit) now read getType() and position once per unit (instead of 4–8 times via getLeft()/getTop()/getRight()/getBottom()) and call package-private Point.getApproxDistance(0, 0, xDist, yDist) directly instead of allocating a temporary Position.
    • Unit.updatePosition(int) only allocates a new Position when ((position.x ^ newX) | (position.y ^ newY)) != 0, avoiding heap allocations and Old-Gen → Young-Gen card-table dirtying for stationary units (minerals, geysers, buildings, sieged/holding units).
  4. Single-Pass getUnitsInRectangle & canBuildHere (Game, Unit):

    • Replaces stream/lambda allocation and 4× getType() calls per unit in Game.getUnitsInRectangle with a direct loop using Unit.overlapsRectangle.
    • Replaces intermediate Position/TilePosition allocations and full getUnitsInRectangle list materialization in Game.canBuildHere with primitive coordinate math and an early-exit ground unit overlap check.
  5. Cached Immutable Return Wrappers (UnitType, TechType, UpgradeType):

    • Pre-builds and caches tileSize(), whatBuilds(), requiredUnits(), abilities(), upgrades(), buildsWhat(), researchesWhat(), upgradesWhat(), maxUnitWidth(), and maxUnitHeight() on UnitType / UnitTypeContainer, as well as whatUses() on TechType and UpgradeType.
    • Replaces internal getTrainingQueue().size() and getTrainingQueue().get(i) calls with allocation-free getTrainingQueueCount() and getTrainingQueueAt(i).
  6. BitSet for Game.visibleUnits (Game):

    • Replaces HashSet<Integer> and visibleUnits.stream() in Game.onFrame with a BitSet and nextSetBit iteration into a pre-sized ArrayList.

JMH Benchmark Results

Measured with bwapi.PerformanceBenchmark on JDK 1.8.0_452 ((4)Andromeda.scx, 356 visible units: 236 stationary resources/buildings + 120 mobile combat units):

Benchmark (bwapi.PerformanceBenchmark) Workload Baseline (master) Optimized (perf-optimizations) Speedup GC Impact
unitGetDistanceToPosition 2,400 calls 27.58 ± 3.61 µs 15.52 ± 2.78 µs -43.7% (1.78×) 0 allocs
unitGetDistanceToUnit 3,600 calls 103.29 ± 26.84 µs 64.01 ± 4.98 µs -38.0% (1.61×) 0 allocs
canBuildHere 96 checks (12 bases × 8 types) 336.88 ± 44.05 µs 232.74 ± 43.41 µs -30.9% (1.45×) 44 → 5 GCs (-89%)
getUnitsInRectangle 32 spatial queries 184.84 ± 45.07 µs 131.03 ± 3.93 µs -29.1% (1.41×) 38 → 28 GCs (-26%)
unitUpdatePositionPerFrame 356 units (236 static, 120 moving) 3.21 ± 0.45 µs 2.65 ± 0.22 µs -17.4% (1.21×) 140 → 54 GCs (-61%)
visibleUnitsWorstCaseOnFrame 700 visible units + 40 fog toggles + onFrame 16.09 ± 1.75 µs 13.30 ± 0.48 µs -17.4% (1.21×) 41 → 12 GCs (-71%)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant