December 22, 2011

How to improve VC++ project build time

How to improve VC++ project build time

Introduction

Microsoft Visual C++ can compile code very quickly, if it is setup properly. There are few basic things to look at:

  • Precompiled headers
  • Pragma once
  • File level parallel building
  • Project level parallel building

Build Statistics

Before we go into the details of each of these items let's see how we can enable some build time statistics. Please note that all my examples and notes are related to Visual Studio 2008 Professional Edition.

Measuring the build time

In VC++ you can get build time by going into Tools > Options > Projects and Solutions > VC++ Project Settings > Build Timing. Set this value to true. This will print the complete build time to the output window. Enable this before you try to improve the build time, then you will be able to measure the performance correctly.

Getting the list of files get included into each compilation unit

In VC++ you can get the list of files gets included into the each compilation unit (CPP) by enabling "Show Includes" in the project properties by going into C/C++ > Advanced > Show Includes. Set this value to true. This will print the included files with the compiler output.

Precompiled Headers

It is often desirable to avoid recompiling a set of header files, especially when they introduce many lines of code and the primary source files that #include them are relatively small. The C++ compiler provides a mechanism for, in effect, taking a snapshot of the state of the compilation at a particular point and writing it to a disk file before completing the compilation; then, when recompiling the same source file or compiling another file with the same set of header files, it can recognize the snapshot point, verify that the corresponding precompiled header (PCH) file is reusable, and read it back in. Under the right circumstances, this can produce a dramatic improvement in compilation time; the trade-off is that PCH files can take a lot of disk space.

Enabling Precompiled Headers

The precompiled-header options are /Yc (Create Precompiled Header File) and /Yu (Use Precompiled Header File). Use /Yc to create a precompiled header. Select /Yu to use an existing precompiled header in the existing compilation. These settings can be changed by going into Project Properties > Configuration Properties > Precompiled Headers.

The idea here is to use one CPP file as the source for creating the pre compiled header and the rest of the CPP files are use the created header file. First you need a header file that every source file in your project will include. In VC++ this is typically called as "stdafx.h". If you do not have one, create it and also the corresponding CPP file which contains a single line to include the "stdafx.h". Change the rest of the CPP files in your project to include the "stdafx.h" file. This include has to be the first include statement (as the first non-comment thing that they do). If you forget to include your precompiled header file then you will get the following error message: "fatal error C1010: unexpected end of file while looking for precompiled header directive."

Now, change the project properties to generate / use pre compiled headers. Make sure you select "All Configurations" so that your fixes will affect both debug and release builds of your project. Select the project properties, go to the C/C++ tab, precompiled headers category. Select "Use precompiled header file" and type in the name of your precompiled header file (stdafx.h). Select "stdafx.cpp" properties, go to the C/C++ tab, precompiled headers category. With "All Configurations" selected, and select "Create precompiled header file", and type in the name of your precompiled header file.

What should be in the stdafx.h?

The precompiled header file should include the big header files which slow down your builds. Possible candidates are STL header files.

The rule of thumb is not to include any header files from your project, since if you modify those header file, the whole project will be rebuilt.

Build with Precompiled Headers

All set now. Try out the build and gather the build statistic and compare with the original.

#pragma once

In the C and C++ programming languages, #pragma once is a non-standard but widely supported preprocessor directive designed to cause the current source file to be included only once in a single compilation. Using #pragma once instead of include guards will typically increase compilation speed since it is a higher-level mechanism; the compiler itself can compare filenames without having to invoke the C preprocessor to scan the header for #ifndef and #endif.

If the header contains only a guard, when compiling the included file (CPP) the included file has to be fully loaded to scan for #ifdef and #endif statements. To improve the time and to support all compilers it is recommended to use both header guards and #pragma once statements in the header files.

For an example, take a header file that you use / include many times form your project. Remove the pragma once from it (if it exists) and compile the project by enabling "Show Includes" discussed previously. Check the number of times the included file gets loaded when the project gets compiled.

File Level Parallel Building

Visual Studio 2008 can take advantage of systems that have multiple processors, or multiple-core processors. A separate build process is created for each available processor. The /MP option can reduce the total time to compile the source files on the command line. The /MP option causes the compiler to create one or more copies of itself, each in a separate process. Then these copies simultaneously compile the source files. Consequently, the total time to build the source files can be significantly reduced. The /MP option can be given to the project settings in the IDE by going into project properties C/C++ > Command Line > Additional Options. The option takes an optional argument to specify the number of processors / cores. If you omit this argument, the compiler retrieves the number of effective processors on your computer from the operating system, and creates a process for each processor. This option does not work with if you have used /Yc (Create Precompiled Header File) or Show Includes. The compiler will warn you, but you can simply ignores it. Otherwise omit the /MP option for /Yc enabled source files (stdafx.cpp) and disable Show Includes option after figuring out the include file fiasco.

With this option along if you are running your compiler on a quad core system, you can gain 3 - 4 times compile time improvement.

Project Level Parallel Building

The number of projects that can build concurrently in the IDE is equal to the value of the Maximum number of parallel project builds property. For example, if you build a solution that comprises several projects while this property is set to 2, then up to two projects will build concurrently at a time. To enable this you need to go into Tools > Options > Projects and Solutions > Build and Run. Set the "maximum number of parallel project to build" to number of processors / cores.

This option only allows you to build non-dependent projects in parallel.

Points of Interest

By doing all of these we were able to improve the build time of a project by 10 times. Hope this article will help you too.

October 25, 2011

Visual Studio add-in: ReviewMyCode

Recently I've written a simple Visual Studio add-in called ReviewMyCode. Here is the summary:

Peer code reviews are a proven way to improve software quality. ReviewMyCode helps you overcome some barriers to this best practice with a simple Visual Studio 2008 add-in that fits your process. The team members can contribute to the review process without leaving the development environment.

The add-in works well within a team development environment by integrating the review process with the SVN source control management system.

July 22, 2011

SVN - Who has created the branch?

I wanted to get the author name who has created a branch in our SVN. With some doco read, I was able to find the answer.
svn log -r 1:HEAD --limit 1 --stop-on-copy
With --limit we can limit the number of results we get from the query, in this case we need only one.

When creating a branch we copy from another SVN location, thus --stop-on-copy comes handy here.

SVN Sparse Directories (partial checkouts)

SVN 1.5 introduces a feature called sparse directories (or shallow checkouts or partial checkouts) that allows you to easily check out a working copy or a portion of a working copy.

How do we do it? On the command prompt use the following commands:
# svn co --depth empty
# pushd
(Assume: foo is a folder in SVN. With the following command you will not get any leaf folders or files.)
# svn up --depth empty foo
(Assume: bar is a sub-folder in foo. With this you will get all from foo/bar)
#svn up foo/bar
(With the following command, you get the only the latest copies of the checked out folders from SVN)
# popd
# svn up
(You can also perform a SVN commit at this level)

Performing SVN checkout (co) for each and every folder you require from SVN is not the way to go, since you cannot do a global SVN update or a commit.



July 21, 2011

HOWTO: Pass more than 10 arguments to a DOS batch file

Batch files can only handle parameters %0 to %9
%0 is the program name as it was called,
%1 is the first command line parameter,
%2 is the second command line parameter,
and so on till %9.

The batch file's limitation to handle parameters up to %9 only can be overcome by using SHIFT.

After NT 4, we do not need to use SHIFT...
FOR %%A IN (%*) DO (
      rem Now your batch file handles %%A instead of %1
)
%* will always expand to all original parameters, sadly. But you can use the following snippet of code to build a variable containing all but the first parameter:

:my_function
rem skip the first arag
shift
set args=%1
:my_loop
shift
if [%1]==[] goto my_end_loop
set args=%args% %1
goto :my_loop
:my_end_loop
rem Do somthing with the args 
goto :EOF

You can call this inside your batch file:
call :my_function %*

After a long silent...

After a long silent I am trying again to post blog posts. Wish me luck...