Friday, October 17, 2008

Don't Be Stupid!

I'm in the process of changing web hosts, so this won't post until probably a few days from now. Midphase was recommended to me, and their basic web hosting package looked like a really good deal, so I took the plunge. Their service has been prompt so far and the controls are way better than my last provider, so I'm happy. On a side note, I still hate the way domain names (and in this case, the transferring thereof) are handled.

But that's not why I'm writing this. No, today I learned an important lesson: don't be stupid.

I have this nifty little "configuration variables" system in my engine. It primarily lets me define key-value pairs in a config file (just like an INI) and access those values by name at runtime, like so:

float Foo = ConfigManager::GetFloat( "Foo" );

There's many reasons why data-driven values are preferred to hard-coded "magic numbers" in games, and I get quite a lot of use out of this system. In fact, as performance on my game began to decline, I suspected it might be due to my rampant use of config vars all over the code base.

What happens when this config var is accessed? The name ("Foo") is passed down to the accessor function, where it is converted into a hashed value (that is, reduced from a string of letters to an integer). That hash can be quickly compared against all the hashed names in the massive table of config vars to find the requested variable. If it is found in the table, the function asserts that the config var is the type that was requested (in this case, a floating-point value) and returns that value to the caller if it's valid. A default value may be used in case the value was not found or was of the wrong type.

A metaphor for non-programmer types: You go to pick up your jacket from the dry cleaners. You supply the clerk with some information about your jacket, and they go and find it on the racks and bring it back to you. But imagine that instead of handing the clerk a stub with a number that matches the tag they left on your jacket, you hand them a piece of paper on which you've written a riddle. When solved, the riddle reveals the number that matches the tag on your jacket. You can still go and get your jacket from the dry cleaners just like before, but now the clerk spends all her time solving riddles. When a flood of customers starts arriving, she's so backed up solving the riddles that she can't deliver everyone's garments in the expedient fashion to which they're accustomed.

Back to programmer land. Solving the riddle is a metaphor for the hash function. If the system is using small, simple hashed values to find the requested items, why are we giving it big, long strings? These are just riddles that it needs to solve before it can do the task that it was meant to do.

So, bringing it all back to that important lesson: don't be stupid. You would never expect the dry cleaners to solve a riddle to find your jacket, and it was just as stupid of me to be hashing strings inside the config var accessor functions instead of just using a hashed value as the given key.

A conclusion! It was an epic two-hour refactoring job to actually fix this in my game. Every case where I used a string literal (like "Foo" in the earlier example) was replaced with a static hashed name so that it would only have to be computed once. This one simple fix doubled my frame rate in Release mode. Don't be stupid.

0 Comments:

Post a Comment

<< Home