Gamasutra: The Art & Business of Making Gamesspacer
arrowPress Releases
November 28, 2014
PR Newswire
View All
View All     Submit Event






If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 
Long build times? You're doing it wrong.
by Bram Stolk on 07/10/14 12:39:00 am   Featured Blogs

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

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!


Related Jobs

NEXON M
NEXON M — Oakland, California, United States
[11.27.14]

Server Engineer - NEXON M (mobile)
Amazon
Amazon — Seattle, Washington, United States
[11.27.14]

Sr. Software Development Engineer - Game Publishing
Deep Silver Volition
Deep Silver Volition — Champaign, Illinois, United States
[11.27.14]

Senior or Principal Programmer
Pocket Gems
Pocket Gems — San Francisco, California, United States
[11.27.14]

Software Engineer - Mobile, Backend & Tools





Loading Comments

loader image