On Optimisations

There’s an old maxim amongst programmers, that premature optimisation is the root of all evil.  Or to put that in a less glib way, it’s generally a bad idea to perform optimisations before your program is completed, and it’s always a bad idea to perform optimisations if you have no way to check your performance before and after.  In practice, this means that you need to use a profiler before you start thinking about performing optimisations.

So apologies, but it’s another technical post.  For those who aren’t interested in programmer topics but are looking for pretty screenshots or discussions about game design, please feel free to skip this one.  Coders, hit the ‘Continue Reading’, below.  :)

There are several different types of profiler;  some need to be built into your program, some are external applications;  there are many different types, which are useful for different things.  Under OS X (which is where I’m currently doing my active development), there’s a free standalone profiler called “Shark”.  “Shark” is a statistical profiler, which means that it stops your program hundreds or thousands times per second, and checks to see what your program is doing at each of those points in time.  Based upon statistical analyses of the data it obtains, it can figure out which bits of your program seem to be taking the longest to run.

There are two things which have been annoying me about MMORPG Tycoon 2 recently;  one is how long it takes for the program to build the world when it starts up (after it opens its window, it takes about five seconds on my laptop before it’s ready to render the world), and the other is how long it takes to shut the game down (quitting takes about two and a half seconds).

For fun, I ran Shark over MMORPG Tycoon today, just to see why these two things were taking so long.  In the current builds, the world terrain is being built using some simple Perlin noise.  While the noise is simple, it’s actually quite expensive to calculate so much of it.  I won’t be using Perlin noise for much longer, so I was curious to see just how much speed I was going to recover when I stopped using all that complex math.  I was also concerned about how long it must be taking to create and set up the terrain quad trees.

As it turned out, though the Perlin noise and the quad trees weren’t very expensive at all.  In fact, there were two major culprits.  The profiler pointed out a really slow piece of code in the model building process;  I had a slow ‘for’ loop copying data from one place to another.  Replacing it with a simple memcpy() fixed that one.  The other culprit was repeatedly re-generating the same pieces of data during the world building process, instead of using data I’d already calculated.  I re-architected the world building to reuse the data which it had already calculated, and that sped things up even more.  The Perlin noise is still definitely slow, but fixing these two other issues brought down the startup time from 5 seconds to about 1.5 seconds.  I’m much happier with that speed!  I had also expected that building terrain quadtrees was going to be a major expense, but it turned out to be extremely minor.

For the shutdown, it turned out that all the time being taken was occurring inside my memory management system (which is what I’d been half-expecting).  What I hadn’t expected to find was actually what was the particular operation that was using up so much time.  The problem was that the VectorStorm engine’s memory management was doing some extensive error checking on each block when it was freed;  making absolutely sure that the engine knew about the block of memory being deallocated before touching it in any way.  While that’s nice for testing purposes, it was really a bit overboard;  removing the silliest of the sanity checking brought the game’s shutdown time down from about two seconds to almost instantaneous.

Moral of the story:  Profile your code before you decide what needs to be sped up.  If I’d just jumped in and started changing the things which I’d assumed were slow, I would never have found several of the big culprits;  they were tiny little things which I’d written many, many months ago;  not the things which actually sounded complicated and slow.