Speeding up build times in Visual Studio 2008

Build times in my current project have been slowly ramping up.

Recently, it took me about 120 seconds to complete build the project from scratch – which can be quite annoying and anti-productive. This is in an early stage of development. I want to cut down on build times as soon as I can.

My first instinct was distributed building. I know of an Israeli solution called IncrediBuild which greatly speeds up build times in organization with many machines. Since my organization has many machines, when compared to the amount of developers, and since most of the machines are idle most of the time – this seemed like a good way to speed things up. However, the solution is very expensive.

I could not find any good free or open source solutions that support Visual Studio 2008. I did discover distcc, which is a good solution for non-Windows development. There is a project under development that aims to add support for Visual Studio 2008 to distcc – this might be a good solution in the future.

In the meanwhile, I decided to explore other ways to optimize the build time. I discovered this lovely article, which pointed my in several interesting directions.

Here are my conclusions.

  1. Use the full capabilities of your CPU! There is a new option in the Visual Studio 2008 C++ compiler named /MP (“Build with Multiple Processes”). Adding /MP to the compiler options cut down the build time on my machine by 60%! Note that it seems that this is only worthwhile on systems with multiple processors or multiple cores. To do this, go to the following property page:

    Project > “Project” Properties… > Configuration Properties > C/C++ > Command Line

    Under “Additional options”, add:

    /MP

    Make sure you also go to

    Project > “Project” Properties… > Configuration Properties > C/C++ > Code Generation

    and disable the “Enable Minimal Rebuild” option, which is incompatible with the “Build with Multiple Processes” option. This might seem like a strange thing to do – disabling the option that is described as “Detect changes to C++ class definitions and recompile only affected source files” – but based on my experience, “Build with Multiple Processes” gives a much bigger performance boost.

  2. Use precompiled headers! They seem like a hassle, but using them correctly, as explained in this lovely article by Bruce Dawson cut compilation time from 53 seconds to 13 seconds in my current project! Obviously, you will benefit much more from precompiled headers if you use a “whale” include file that is static (non-changing), like “Windows.h”, various STL headers and – in my case – the ACE headers. More about this issue, in a more user friendly way, can be found here.

  3. Employ smarter physical code design. This is based on ideas documented by John Lakos in his wonderful book Large Scale C++ Software Design, which – while outdated nowadays – still is a worthy read. You can learn a lot about this in these two articles in Games from Within. Remember, however, that using these techniques can sometimes impair the clarity and brevity of your code. I made some small changes in my code – and cut down the build time by another several seconds.

    All in all, after employing all of these techniques – I managed to cut down the build time from ~120 seconds to ~10 seconds. Pretty cool for 30 minutes of research and coding.

"If" conditional does not work properly

While debugging, I noticed that an “if” conditional displayed some pretty strange behavior.

I had code similar to this:

if (SomeFunctionReturningBoolean() == true)
{
// do something…
print “1”;
}
else
{
// do something else.
print “2”;
}

Strangely, even though SomeFunctionReturningBoolean() returned true (as the debugger helpfully noted) – the “else” clause was executed, rather than the “if” clause.

I started investigating this.

First, I suspected a mismatch between the code and the executable. I rebuilt from scratch, but no dice. I manually cleaned all of the intermediate files, nope – same strange behavior.

I then changed the code to the following:

bool fResult = SomeFunctionReturningBoolean();

if (fResult == true)
{
// do something…
print “1”;
}
else
{
// do something else.
print “2”;
}

fResult clearly held “true”, but “2” was still printed.

Next, I changed the code to:

bool fResult = SomeFunctionReturningBoolean();

if (fResult == true)
{
// do something…
print “1”;
}

if (fResult == false)
{
// do something else.
print “2”;
}

Neither “1” nor “2” were printed. Strangely, this seemed to work:

bool fResult = SomeFunctionReturningBoolean();

if (fResult)
{
// do something…
print “1”;
}
else
{
// do something else.
print “2”;
}

it printed “1”. I started wondering whether the function was indeed returning true. I changed to disassembly mode, and to my surprise – I noticed that the function was returning 0x6E, and that the equality check was:

cmp eax, 1

Going deeper into the code, I discovered that a function used by SomeFunctionReturningBoolean() as a basis for its return coded simply did not return any value. The programmer was annoyed by some of the warnings returned by an external include file, and disabled all of the warnings using a pragma – so the “Not all code paths return a value” warning simply did not appear when I compiled the code.

The Visual Studio 2008 debugger treats a bool variable with a value of 0 as false, and it displays “false” as its value in any variable windows. If the bool variable contains anything but 0 – the value displayed is “true”. This is generally correct, since non-zero values are handled as “true” by most conditional assembly instructions. However, a comparison instruction like

if (variable == true)

is compiled to

cmp eax, 1

which does not check for “true”-ness, but rather for equality with 1 – which is the value that “true” signifies in the current Microsoft C++ world.

So – the actual source code compared the bool to a specific value – 1, and since 0x6E is not equal to 1 – the check failed and we executed the “else” clause.

So, what did we learn from this issue?

  1. Never disable all of the warnings using a pragma – disable specific warnings, otherwise you’ll lose important warnings that might appear.
  2. The VS2008 debugger displays “true”/”false” values in the variables window based on a check of “true”-ness, not an equality to the “true” value.
  3. The “Go to assembly” can be very, very useful at times. You should familiarize yourself with it.