Monthly Archives: December 2015

Preprocessed Strings for Asset IDs

Mick West posted up on his site a really good overview of some different methods for hashing string ids and gave good motivation for optimizing this area early on in a project. Please do review his article as it’s a prerequisite to this post, and his article is just really good.

I’ve been primarily concerned with memory management of strings as they are extremely hairy to work with. For me specifically I’ve ruled out the option of a string class — they hide important details and it’s too easy to write poor (but functional) code with them. This is just my opinion. Based on that opinion I’d like to achieve these points:

  • Avoid all dynamic memory allocation, or de-allocation
  • Avoid complicated string data structures
  • Little to no run-time string traversals (like strcmp)
  • No annoying APIs that clutter the thought-space while writing/reading code
  • Allow assets to refer to one another while on-disk or in-memory without complicated pointer translations

There’s a lot I wanted to avoid here, and for good reason. If code makes use of dynamic memory, complicated data strctures, etc. that code is likely to suck in terms of both performance and maintenance. I’d like less features, less code, and some specific features to solve my specific problem: strings suck. The solution is to not use strings whenever possible, and when forced to use strings hide them under the rug. Following Jason Gregory’s example he outlined about Naughty Dog’s code base from his book “Game Engine Architecture 2nd Ed” I implemented the following solution, best shown via gif:

anim

The SID (string id) is a macro that looks like (along with a typedef):

I’ve implemented a preprocessor in C that takes an input file, finds the SID macro instances, reads the string, hashes it and then inserts the hash along with the string stored as comment. Pretty much exactly what Mick talked about in his article.

Preprocessing files is fairly easy, though supporting this function might be pretty difficult, especially if there are a lot of source files that need to be pre-processed. Modifying build steps can be risky and sink a ton of time. This is another reason to hammer in this sort of optimization early on in a project in order to reap the benefits for a longer period of time, and not have to adjust heavy laid-in-stone systems after the fact.

Bundle Away the Woes

I’ve “stolen” Mitton’s bundle.pl program for use in my current project to recursively grab source files and create a single unified cpp file. This large cpp can then be fed to the compiler and compiled as a “unity build”. Since the bundle script looks for instances of the “#include” directive code can be written in almost the exact same manner as normal C++ development. Just make CPP files, include headers, and don’t worry about it.

The only real gotcha is if someone tries to do fancy inclusions by defining macros outside of files that affect the file inclusion. Since the bundle script is only looking for the #include directive (by the way it also comments out unnecessary code inclusions in the output bundled code) and isn’t running a full-blown C preprocessor, this can sometimes cause confusion.

It seems like a large relief on the linker and leaves me to thinking that C/C++ really ought to be used as single-translation unit languages, while leaving the linker mostly for hooking together separate libraries/code bases…

Compiling code can now look more or less like this:

First collect all source into a single CPP, then preprocess the hash macros, and finally send the rest off to the compiler. Compile times should shrink, and I’ve even caught wind that modern compilers have an easier time with certain optimizations when fed only a single file (rumors! I can’t confirm this myself, at least not for a while).

Sweep it Under the Rug

Once some sort of string id is implemented in-game strings themselves don’t really need to be used all too often from a programmer’s perspective. However for visualization, tools, and editors strings are essential.

One good option I’ve adopted is to place strings for these purposes into global table in designated debug memory. This table can then be turned off or compiled away whenever the product is released. The idea I’ve adopted is to allow tools and debug visualization to use strings fairly liberally, albeit they are stored inside the debug table. The game code itself, along with the assets, refer to identifiers in hash-form. This allows product code to perform tiny translations from fully hashed values to asset indices, which is much faster and easier to manage compared to strings.

This can even be taken a step further; if all tools and debug visualizations are turned off and all that remains is a bunch of integer hash IDs, assets can then be “locked” for release. All hashed values can be translated directly into asset IDs such that no run-time translation is ever needed. For me specifically I haven’t quite thought how to implement such a system, and decided this level of optimization does not really give me a significant benefit.

Parting Thoughts

There are a couple of downsides to doing this style of compile-time preprocessing:

  • Additional complexity in the build-step
  • Layer of code opacity via SID macro

Some benefits:

  • Huge optimization in terms of memory usage and CPU efficiency
  • Can run switch statement on SID strings
  • Uniquely identify assets in-code and on-disk without costly or complicated translation

If the costs can be mitigated through implementing some kind of code pre-processor/bundler early on then it’s possible to be left with just a bunch of benefits :)

Finally, I thought it was super cool how hashes like djb2 and FNV-1a use an initializer value to start the hashing, typically a carefully chosen prime. This allows to hash a prefix string, and then feed the result off to hash the suffix. Mick explains this in his article this idea of combining hashed values as a useful feature for supporting tools and assets. This can be implemented both at compile or run-time (though I haven’t quite thought of a need to do this at compile-time yet):

 

TwitterRedditFacebookShare

I hate the C++ keyword auto

Warning: rant post incoming!

I’m not really sure what other programmers are thinking about or focusing on when they read code, but personally I’m thinking mostly about the data being operated on. One of the first things I like to figure out when reading new code is if any C++ constructors, destructors, or other features that generate code are being used with a current data type.

If a piece of code is operating on only integers usually it becomes easy to have a grounded concept in what kind of assembly is to be generated. The same goes for code operating on just floats, or in general just POD data. The moment C++ features are used that generate large amounts of code at compile-time “surprises” can pop out in the form of constructor code, destructor code, implicit conversions, or entire overloaded operators.

This bugs the crap out of me. When I look at code I rarely want to know only what the algorithm does, and instead need to also know how the algorithm operates on given data. If I can’t immediately know what the hell is going inside of a piece of code the entire code is meaningless to me. Here’s an example:

Please, tell me what the hell this for loop is doing. What does that = operator do? Is there a possible implicit cast when we pass results to the StoreResults function? What is .begin, what is i, and how might the ++ operator behave. None of the questions can be answered [to my specific and cynical way of thinking about C++] since this code is operating on purely opaque data. What benefit is the auto keyword giving here? In my opinion absolutely none.

Take a non-auto example:

In the above code it is painfully obvious that the only opaque pieces of code are functions, and the functions are easy to locate at a glance. No unanticipated code can be generated anywhere, and whenever the user wants to lookup the Results struct to make sure there are no “C++ loose ends” in terms of code-flow a simple header lookup to find the Results declaration is all that’s needed. I enjoy when code is generated in a predictable way, not in a hidden or hard to reason about way. The faster I can reason about what this code will do at run-time the faster I can forget about it an move onto something else more important.

Code with unnecessary abstractions bugs me since it takes extra time to understand (if an understanding can even be reached), and that cost better come with a great benefit otherwise the abstraction nets a negative impact. In the first example it’s not even possible to lookup what kind of data the results variable represented without first looking up the SortByName function and checking the type of the returning parameter.

However, using auto inside of (or alongside) a macro or template makes perfect sense. Both macros and templates aim at providing the user with some means of code generation, and are by-design opaque constructs. Using auto inside of an opaque construct can result in an increase in the effectiveness of the overall abstraction, without creating unnecessary abstraction-cost. For example, hiding a giant templated typename, or creating more versatile debugging macros are good use-cases for the auto keyword. In these scenarios we can think of the auto keyword as an abstraction with an abstraction-cost, but this cost is side-stepped by the previously assumed abstraction-costs of templates/macros.

Please, stop placing auto in the middle of random every-day code. Or better yet, just stop using it at all.