Site Sections

Friday, October 26, 2007

Optimizations - Simple and Effective

I was working on my engine the other night, trying to get my new managers to work with a simple map parser that I am working on (I will be putting up the tutorials on the managers in a few days), and I noticed that I was starting to get some major slow down while rendering models, especially when I got any more than say 20 objects on the screen. Lag city I tell ya! It was really depressing.

Time to optimize...

C#, like many languages (especially C/C++) give you the option to pass values by reference. This means that instead of "passing by value" meaning copying the contents of a variable into another stack variable created for the called functions parameters, we instead simply create a reference (or pointer) to the location in memory where the passed variable is stored. This works great for larger objects as we do not want to have to create a separate copy of every object we want to pass as a parameter, especially if we are passing the object down a couple of calls.

This was my first optimization that I noticed I could perform. Now C# has two forms of pass by reference when dealing with function calls:
  1. Simply to pass a reference to a function by identifying it with the "ref" keyword, this must be placed in the function definition and the function call.
  2. Passing return variables by reference, this requires the use of the "out" keyword before the return variable INSIDE of the parameter list. We do not return these variables as normal, instead we can assign them a value and it acts as if we are assigning the value to the variable from the calling function scope. This is much faster than using standard returns when using them for assignment operations, also it allows for multiple return values. I have modified my managers to use this form of returning when doing searches for objects contained within.
This sped up the Hazy Mind engine quite a bit on its own. I did notice a major improvement when it came to the return speed on the shader manager. This got me thinking as to why the shader manager was giving me such poor performance. I started looking around and I noticed that in all the draw loops for objects, the Hazy Mind engine uses the shader manager to request the shader to draw with every time through the draw loop.

Hmmmm, I think to myself, why are we doing this? I am assuming it was in case someone wanted to change the shader of an object in the middle of the game? Well why not then move this expensive search cost to the setShader function and store a "reference" to these objects locally. Small memory hit, vast performance increase. We amortize the cost of searching to constant (actually practically nothing since we shouldn't need to change shaders on specific objects very often) and tada! almost instant improvement in speed, I can have many more objects on the screen with no lag. I have my render framerate fixed and it doesn't jump at all. I can imagine that my unfixed frame rate would now be through the roof.

So key things to remember:
  1. Use pass by ref, especially in functions that you are not going to change the state of the parameter objects. It really isn't all that much of a benefit on basic data types as their size is pretty much the same as the size of the reference.
  2. Use out return parameters for managers that return bulky objects such as models.
  3. keep search times low by not putting them in draw loops (this is very very important!)
Hope this helps!

No comments: