Crash-tan
Even more performance in 1.61.3!
Crash BASIC 1.61.3 Release Notes
written by Crash-tan
Last time was about crowds. This time the crowd bumps into itself.
Give a few thousand sprites an ONCOLLISION handler, let them drift through
each other, and 1.61.2 slowed to a crawl. It didn't crash. It just checked
every sprite against every other sprite on every tick until it could barely
move. A field of 3,000 small overlapping squares, each one changing its frame
while it touches something, got about 10 updates a second. At 6,000 it got
about 3.
That same scene now runs at about 60 updates a second with 3,000 squares, and
about eight times as fast as before with 6,000. Plain behavior crowds with no
collisions got quicker too, and so did every SUB and FUNCTION call. Nothing
in your program has to change.
All the comparisons below come from the same Mac, running 1.61.2 and 1.61.3
back to back on the same scene.
COLLISIONS ONLY CHECK THE NEIGHBOURS
To find out who is touching whom, the engine used to take every sprite with anONCOLLISION or AFTERCOLLISION handler and test it against every other
sprite in the scene, twice for each pair, once from each side, sixty times a
second. For 2,000 sprites that is four million checks a tick, and almost all of
them were between sprites on opposite sides of the screen.
Now the engine sorts sprites into a grid by where they are and only compares
sprites that share a square. Two sprites across the room from each other never
get compared at all. Sprites that span a lot of the screen, like a long wall,
are still checked against everything, so nothing big slips through.
The collisions reported are exactly the ones you got before: the same pairs,
the same tag_b$, the same edge$, the same impact velocities, on the same
ticks. There are just far fewer checks behind them.
HANDLERS, SUBS AND FUNCTIONS RUN FASTER
A collision handler is ordinary Crash BASIC, and with thousands of sprites
bumping into each other it can run hundreds of times a tick. Running one had
more overhead than it needed:
- Every call copied the routine it was calling. Finding a
SUBorFUNCTIONby name made a copy of all of it, parameters and statements
included, then threw the copy away after the call. Calls now use the one
stored copy. - Every call built its local variables a new place to live, and threw it
away on return. That space is now emptied and reused. - Looking up a variable name did more work than it needed, and a program
looks up variable names constantly. It does less now.
A tight loop that calls a small SUB two million times now finishes in about
half the time it did in 1.61.2.
The rules didn't move. A variable you never set still reads as 0, and a SUB's
locals are still gone when it returns, so the next call starts fresh.
Changing one thing about a sprite got cheaper too. SET SPRITE n FRAME 2,
or any SET SPRITE that only changes a few settings, used to copy everything
the engine knows about that sprite just to read a handful of settings back out.
It reads only what it needs now. It still sends the sprite's complete look,
position, image and all, so in a multiplayer game a player who joins later
still sees every sprite exactly as it is.
A handler like this one, which flashes a sprite while it touches another,
is exactly the kind of code that got faster:
BEHAVIOR "drift"
BVELOCITY @vx = 1, 0
ONCOLLISION
IF index_b >= 0 THEN SET SPRITE index_a FRAME 1
END ONCOLLISION
AFTERCOLLISION
IF index_b >= 0 THEN SET SPRITE index_a FRAME 0
END AFTERCOLLISION
END BEHAVIORBEHAVIOR CROWDS GOT ANOTHER BOOST
Even with no collisions at all, the engine spent part of every tick looking up
each behavior's settings by name, spelling out the name in capital letters
first, for every sprite, every tick. It now knows where each setting lives
without looking it up.
A crowd of BOSCILLATE sprites with nothing touching anything now gets about
one and a half times as many updates a second as it did in 1.61.2, from 5,000
sprites up to 12,000.
WHO NOTICES
If your sprites collide a lot, especially bullets, swarms, particles with
handlers, or anything where hundreds of things touch at once, this is the big
one. Scenes that used to lurch should now keep up.
If your game calls a lot of SUBs and FUNCTIONs in tight loops, it gets
quicker even with no sprites on screen.
If your game has a handful of sprites and a few handlers, you won't see a
difference, and nothing in how your game behaves has changed.