← All Solo, Not Alone posts

ai-coding performance

The Benchmark Report That Guessed the Wrong Cause

A headless bench found Earfh's collision pairs growing about 125 times for 13 times the enemies. The report's explanation sounded right and was wrong. Measuring before and after is what caught it.

Earfh is capped at 15 enemies on screen, and on 2026-09-27 the open question was whether a horde mode was possible. An agent wrote a headless benchmark that spawns 15 to 200 enemies around the player and averages 180 physics ticks of Godot's own performance monitors, and a CI workflow to run it. The first honest result was a table with one number that did not look right.

Going from 15 enemies to 200 is 13.3 times more enemies. The count of 2D collision pairs went from 111 to 13,875, about 125 times more. The report calls that close to quadratic and moves on to explain it.

The explanation that read well

The report checked the obvious suspect first. Enemy bodies are configured to collide with the player and walls, not with each other, so this was not enemies pushing on enemies. Then it offered a mechanism. Every enemy chases the same target with nothing keeping them apart, so a big crowd packs into one small patch of space around the player, and Godot's broad phase has to sort through every overlapping bounding box before it even looks at layers. The recommendation followed from that: add a separation nudge so the crowd stops stacking on a single point.

It is a good paragraph. The numbers are consistent with it, the physics is real, and the fix it recommends is a sensible thing to build. It was also a guess, and the report did not label it as one. It says "the likely mechanism" once and then ranks its recommendations as if the mechanism were known.

What the pairs were

Each enemy also carries a detection area, the zone that tells it the player is close. That area was sitting on the player's collision layer at Godot's defaults, monitoring and monitorable both on. In the broad phase it paired with every enemy body and every other enemy's detection area that it overlapped. The body masks the report checked were fine. The areas were a different object with a different configuration, and nobody had looked at them.

The design spec for the horde engine, written the same day, replaced the report's explanation with this one and called it the likeliest cause. The first task in the plan was a small change that tested the claim instead of trusting it. It put every detection area on no layer, masking only the player, and not monitorable. Then the bench ran again on the same machine with the same seed, one run right after the other.

At 200 enemies the pairs fell from 13,875 to 33. At 100 enemies while firing, physics time fell from 25.3 ms per frame to 6.5. At 15 enemies, where there was little to fix, physics time went up slightly, from 2.36 to 2.79 ms, and the report says plainly that both numbers are small and noisy.

The part that stayed in the file

The research doc was not rewritten. The wrong paragraph is still in it, followed by a section headed "After the detection-area fix" with the before and after tables. Anyone reading it later can see the guess, see the measurement that replaced it, and see how far apart they were.

The separation pass got built anyway, because horde mode still wanted enemies not to sit on top of one another. It cost something. Physics time at idle rose between 23% and 51% on the 50 to 200 enemy rows, and the 150-enemy row went over the 25% bar that the plan had set in advance for stopping and reporting. A cap of eight neighbors per enemy brought the 150 and 200 rows back to between 5% and 19%. The pair count stayed flat through all of it, as expected, because separation moves positions and does not touch layers. Separation alone would have shipped as the answer to a problem it does not cause.

The report's other headline, that firing into a crowd was the single biggest cost, did not survive either. Once the detection areas were fixed, a profile of 100 enemies idle and under fire found that firing cost no more than the idle crowd. The plan's fixes for hit resolution did not apply, and none were made.

What I changed

A performance report from an agent now has to say which of its claims were measured and which were inferred, and a recommendation built on an inference gets the same weight as the inference. The cheapest way to find out is the one that happened here. Make the smallest change that would confirm the story, run the same bench twice, and keep both tables.

This is the pattern with agent-written analysis in general. The prose is fluent whether the claim underneath was measured or guessed, so fluency tells me nothing. When I am the only person reading the report, the question I ask is what the author would have seen if the explanation were wrong.

More on the game at /games/earfh, and the rest of this series at /notes.

← All Solo, Not Alone posts