{"id":2269,"date":"2011-10-09T18:02:21","date_gmt":"2011-10-09T08:02:21","guid":{"rendered":"http:\/\/www.vectorstorm.org\/?p=2269"},"modified":"2011-12-02T23:18:22","modified_gmt":"2011-12-02T13:18:22","slug":"profiling-is-fun","status":"publish","type":"post","link":"https:\/\/www.vectorstorm.com.au\/2011\/10\/09\/profiling-is-fun\/","title":{"rendered":"Profiling is fun"},"content":{"rendered":"
Actually, that’s a dirty lie.<\/p>\n
Profiling is fun when it shows you something big that’s easy to fix. \u00a0Profiling is a lot less fun when everything is equally slow. \u00a0But luckily in my case, there was something big to fix. \u00a0:)<\/p>\n
I noticed the other day that editing roads in MMORPG Tycoon 2 had become extremely slow. \u00a0(Well.. actually I noticed that roads didn’t draw at all. \u00a0It was only after I fixed them not drawing that I noticed that they were slow to edit). \u00a0 This was odd, because I have videos of early road editing, where it hardly touched the frame rate at all on a system much slower than the one I’m using now. \u00a0So after some profiling, I found that creating their procedural geometry was taking up about 75% of the whole processing time for the frame when they were created. \u00a0This was extremely bad when editing them, especially (because they’re regenerated every frame, when they’re being edited, or when the ground under them is being edited).<\/p>\n
Those of you who were at Freeplay will remember the part of my talk where I discussed optimising games, and when to do it (or rather, when not to do it).<\/p>\n
For those who weren’t there, the tl;dr version is this: \u00a0don’t optimise anything unless you have timing numbers that proves it’s slow relative to the rest of your program, and against which you’ll be able to judge your progress. \u00a0Ideally, you get these numbers by using a profiler of some sort. \u00a0(Visual Studio has one built in, modern Xcode uses Instruments while old Xcode used Shark, under Linux you have gperf, etc)<\/p>\n
What I didn’t say in that talk (and which I’ve regretted ever since), is that there’s a second criteria which can make it acceptable to optimise some code, even if you don’t have performance numbers. \u00a0Here is that other criteria:<\/p>\n
<\/p>\n
The Monte Carlo test is basically very quick and ad hoc profiling. \u00a0Profiling does exactly this same process, but instead of doing it ten times, it does it millions of times, far faster than you’re able to do it manually. \u00a0It avoids having to go to the bother of setting up proper profiling, and will still point out anything that’s extremely slow in your code.<\/p>\n
But back to my story. \u00a0In my case, I did do real profiling. \u00a0Here’s what it looked like in Instruments:<\/p>\n
<\/a> As you can see from the screenshot, it’s spending about 30% of the frame time in vsMeshMaker::BakeTriangleVertex, 20% of its time in vsFloor (called from vsMeshMaker::BakeTriangleVertex), 16% of the frame time in vsVector2D::SqLength() (also called from vsMeshMaker::BakeTriangleVertex)… \u00a0you get the idea. \u00a0Add it all up, and we were spending about 75% of our CPU time inside BakeTriangleVertex. \u00a0That BakeTriangleVertex function was really, really heavy for some reason, and I needed to figure out why.<\/p>\n Basically what vsMeshMaker::BakeTriangleVertex does is to look at all the triangle vertices in a procedurally generated model, and to decide (one at a time) whether they should be merged with other nearby vertices to make a smoothly curving surface. \u00a0The hardest part of this is actually finding other nearby vertices. \u00a0Previously, I had sped this up by dividing up space into grid squares, and storing each vertex into the appropriate grid space. \u00a0Then when I wanted to know about nearby vertices, I only had to check the other vertices in the same grid space, not every other vertices in the model.<\/p>\n But as it turns out, those grid spaces were too large, especially for large objects like roads, and so I was still comparing far too many vertices for merging. \u00a0So I increased the number of grid spaces (and made each grid space smaller, so there would be fewer vertices to test in each), so that instead of having an 8x8x8 grid and needing to test every vertex in those large squares, we have a 32x32x32 grid and only need to test every vertex in much smaller squares. \u00a0I had also noticed that very simple functions like vsFloor and vsVector3D::SqLength() were showing up in the profiling, and so made those functions inline, to hopefully reduce the cost of calling them. \u00a0This led to this graph:<\/p>\n
\n<\/a>
\n<\/a>That graph is of CPU usage. \u00a0(Also, it totally breaks the site frame layout, but I don’t care. \u00a0I want you to see the details. \u00a0:) ) \u00a0The spike at the start is the game starting up (and generating all the world geometry all at once). \u00a0The low part in the middle is just me looking around a bit, and then the heavy bit that I have highlighted is me editing a road — just dragging one of its endpoints around the map at random.<\/p>\n