Friday, March 19, 2010

Game Logic VIII : optimising

When optimising code from an OO base, I find that one of the problems I face more often than not is the tendency for the code to have just in time value calculation. This was really cool when I started out in coding, even a year ago I would have advocated lazy evaluation whenever possible, but now I'm not so sure. The issue with lazy evaluation is the inherent additional state data about the value, and the I cache miss for when it does need evaluating.

Pushing your data around in rows and transforming it from one state to another, you'll never hit the dreaded I-cache fail that lazy evaluation necessitates, but, you can still have lazy evaluation. Huh?

Simple, it's existence based processing again. One of the input tables you need for a process can be a dependency table, this dependency table is your lazy evaluator friend. All it needs to do is check to see if a value doesn't already exist, then add to the table of values to calculate, then process that table, then voila, you have your lazy values without having to actually break away from your main thread, because your main thread doesn't do too much before it demands the values.

Also, optimising is quite a bit easier because you know what to optimise. How often have you looked at some code and had an "aha" moment, because you've noticed that the real bottleneck was something daft like "if( gDoDebugRenderingOfThisOneThing )", which is not a const bool, so it doesn't compile out of your release build? For me, often enough to say that debug code and runtime code (processing of the game state vs processing of views on the data) should be kept separate.

Optimising processes or transforms is also a lot easier than optimising great spiderweb like connected runtime dynamic function messes that I've found in scene-graphs, game-logic and AI code paths. Humans can reason about single transforms a lot easier than they can reason about complex nested dependencies.

Finally, ifs are really bad for your code, and processing data through existence lists gives you a lot of the power of ifs without the failure overhead. The principle behind my process for removing ifs is to provide a ifless list structure that uses branchless value update techniques to write to link->next pointers. The only ifs you have to use are the "is list terminated yet" type.

By the way, thanks for reading all these posts. I hope it helps. Its helped me understand my own idea better to write it out. I think it's time for me to write up a complete document now.

No comments: