Sponsored By

Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs.

Long build times? You're doing it wrong.

If your game takes a long time to build, you are simply doing it wrong. You've most likely created a big ball of mud. The remedy is simple: don't include header files in header files.

Bram Stolk, Blogger

July 10, 2014

3 Min Read

When I hear developers complain about long build times, it immediately makes me suspicious about the quality of their code base. They have simply built a big ball of mud, a.k.a. spaghetti code. What has happened is that the compiler gets bogged down by an avalanche of include files. Don't fight the symptom by using precompiled headers! Instead, fix the root cause: the crappy architecture of your game. What follows in this article, is a recipe for a clean code base that builds in a couple of seconds, not minutes.

Here's the TL;DR version of this article: do not include header files in header files. The dependencies that foo.cpp uses to implement its functionality should never leak into the foo.h interface. I'll state up front that a C-like programming style lends itself better for this than a C++ programming style. But even when using C++ classes, it is often possible to avoid include directives.

If the interface of class Foo only contains uses of class Bar that are references or pointers, then the compiler does not need to know what Bar looks like when parsing the header for Foo. All that is required is that Bar is forward declared for the Foo interface. You tell the compiler that there is a class named Bar, and leave it at that. Typically, there will be some classes in your game that need to include large third party frameworks. It is paramount that these frameworks do not leak into the users of class Bar, like class Foo.

If you constantly ask yourself: "How can I keep the implementation details out of the interface?" then you can cut down on the include directives. Also preferring primitive types char/int/float/bool and pointers over custom object types is a great strategy for pruning include hierarchies.

So where can this get you? To illustrate this, I will examine the code base of the game I am currently creating. The only uses of middleware are OpenAL and OpenDE. It is a fully featured game with biplanes, AI, formation flying, dogfighting, explosions, menus, tutorials, whathaveyou, totaling roughly 50K lines of code. But building from scratch (including linking) takes less than three seconds.

real    0m2.687s
user    0m5.076s
sys     0m0.542s

This is because the include hierarchies are extremely flat. For instance, here is what gets pulled in to implement the logical representation of my game world.

And here is what the code that uses the game world sees when it includes wld.h:

In my code, dependencies are hidden, and almost never propagated throughout the system. The notable exceptions are the leaking of my vector math header and my rendering context data into the interface. The latter includes no further files, and the former only three system headers.

The world contains terrain, planes, bullets, hangars, explosions and much more. But the code that uses (creates, destroys, renders) this world should not be bothered by these details.

Personally I think that distributed build systems, build caches, precompiled headers are all curses disguised as blessings. These mechanisms that make your build go faster, allow you to ignore the root problem for longer. The mud ball grows and grows, and when you are finally fed up with long build times, the tangled mess may be beyond repair. Top tip: disable precompiled headers!

Let me conclude with a tip on how to inspect the sanitation conditions in your code. Snowballing include directives are brutally exposed with the 'Doxygen' tool if you tell it to generate include graphs with graphviz's dot program. Use the following directives in your Doxygen configuration file:

DIRECTORY_GRAPH = YES
INCLUDE_GRAPH = YES
HAVE_DOT = YES

If the Doxygen output includes huge dependency graphs, you know that you are on the wrong path. Also, cycles in the graph with lower level files pointing back to higher level files are a huge red flag.

I'm curious to know how my peers view this issue. Also, what is the longest build time that you have encountered? Drop your remarks in the comments, they are highly appreciated!

Read more about:

Featured Blogs

About the Author(s)

Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like