January 04, 2005

Improving code turnaround time

I have been using refactoring, as described in Martin Fowler's excellent book, for a couple of years now and found that it has improved my productivity and the quality of my code considerably. One of the basic requirements of refactoring is that you must be do it incrementally. That is, after each individual change, you must build and test your code, ideally using automated tests. I will call the time it takes to make a simple modification to the code, built it and test it the "turnaround time". Quick turnaround is also essential to another approach to programming called Test Driven Development or TDD.

There is a major problem with applying these techniques to games development. Build times for most games these days is anything but fast. A build time of 3 minutes would be quite fast, even for a code change that affects a small number of source files. See Noel Llopis's articles on C++ physical structures for some tips on improving the build times of C++ programs.

Unfortunately, even using all of the techniques he describes, I don't think I will be able to get build times as fast as I would like so I can write games the way I really want to. To program efficiently, it will be necessary to make more than one code change step per build / test iteration, as I have to do now on my current game project.

As I see it, the problem is quite fundamental to C++. From C it inherits the use of header files and a separate linking step in order to achieve source-file level modularity and incremental builds. This approach simply does not scale well to current games projects. As games platforms have increased in power, the games we write for them have increased in size. Although the power of the computers we use to build them has also increased, every year we see build times get longer and longer.

If you have read any of my previous posts, you will probably not be surprised that I introduce C# at this point. C#, unlike its ancestors C and C++, does not use header files or a separate link step. And this pays off. It's build times are orders of magnitude shorter. As an experiment, I built a mid-sized C# program, probably about the same size as a typical game these days, 8MB of source code in total. A full rebuild took 38 seconds. A dependency check, with no files modified took 3 seconds. These are the kinds of build times I need to apply refactoring effectively.

I am writing a C# to C++ translator. My idea is to translate C# into C++ and then use a C++ compiler and linker to generate native code for a games platform. Although I think I can generate the C++ code in such a way as to reduce build times, for example by minimizing header dependencies and using forward declaration wherever possible, it will still take significantly longer than C# compilation.

But I've had another idea. Although I need to compile to native code in order to run on a games platform such as a console, I can run the C# program, prior to translation to C++, on any platform with a .NET virtual machine, e.g. a Windows PC. So my idea is to perform several programming iterations only building and testing for a .NET target platform. Having performed several iterations, I will build for my actual games platforms and test on those, ideally using a build farm that will automatically build, deploy and test for me, while I get on with the next batch of iterations. I think this could work very well for the majority of code, which is not tied to features specific to a particular games platform.

Obviously, even if C# is used in a games project, a lot of code will still need to be written in a language like C++, I would estimate up to 50%, depending on the project and the platforms. This code will not run on a .NET virtual machine. Fortunately the .NET framework offers many ways to allow "managed" code to link with "unmanaged" code like C++. In Microsoft's implementation, this might be done using Managed C++. So the C++ game code might be compiled to a Windows DLL that would be loaded by the .NET virtual machine and linked with the C# code being tested.

On defense of C++ build times, it doesn't have to be that way. I'm not denying that C# is much faster on that respect, but it's possible to achieve perfectly fine turnaround times with C++.

One of the most pleasant programming experiences in the last few years is when I was working on some subsystems using TDD (Test-Driven Development). I had it set up so I only liked against the minimum number of libraries necessary (as opposed to pulling the whole game engine). I also had it set up so my test project was dependent on the library I was working on, and, as a post-build step, it executed itself to run the tests.

I was able to make a change to a single cpp file, initiate a build, and see the results of the tests in less then 4-5 seconds. With a setup like that, it was really like being in heaven. My fingers would twich over to F7 every so often, even if I hadn't changed any code and I was just talking to someone :-)

If you're working on much higher level code (and at that point, it's worth asking if it has to be done in C++), it's also possible to achieve great iteration times by using DLLs (which would have to be replaced with static libraries or some sort of overlays on console platforms that don't support them).

There's always "edit and continue" if you're using Visual Studio, but I've only had bad experiences with it, even though I know some people who swear by it. To me it feels just as broken as incremental linking (which stops working as soon as you modify a library--which is where 99.99% of my code always is).
Well I hope you're right because I think C++ will remain at least one of the primary languages for games development over the next few years. Although I believe you that, with discipline, it is possible to get quick builds at least for lower level C++ code, this is a good example of another of the unnecessary burdens C++ places on programmers.

From the principles of the Agile Manifesto:

"Simplicity - the art of maximizing the amount of work not done - is essential"

Build times are truly the bain of any large project, and from my experience it seems the games industry suffers the most, with its early adoption of new technology and spiralling project sizes (and short deadlines!) When I worked at Argonaut Games we tried everything to keep build times down, including awful things like #includeing everything into one file and compiling that. Sometimes this gave great results, but for my money it didn't solve the main problem, people needlessly including files instead of pre-declaring them. Designing the code from the start to adhere to the ideas in John Lakos's excellent book "Large-Scale Software Design" helped one of our projects, keeping dependencies to a minimum, but it's hard to stick to with deadlines and large teams not helping.

After the experiences at Argonaut I was convinced there was a better way of keeping track of these things and set out to make tools to help coders in their day-to-day lives. One of those tools is IncludeManager - a Visual Studio Add-in that shows your header file dependencies and interactively updates while you edit your code. Bottlenecks are made really clear when you have a full dependency graph! There's a 14 day trial available from our web-site.

Keeping the build times down is difficult, but it is achievable, and having a code time turnaround in seconds instead of minutes revitalises coders' enthusiasm and makes tweaking code a pleasure and not a chore - the kinds of changes you make in games where that last tweak can make the difference between an 'ok' game and award-winning game-play!

Your blog is awesome, nice, informative and very interesting for our visitors. I hope our services will be helpful for your visitors. Our services include good, quality professional web design services, psd to html conversion,Psd to html slicing, Psd to wordpress, ecommerce website development, seo, data entry,windows mobile apps, web research.
I agree that your frequent-testing methodology will decrease turnaround time in the end. I work in game programming and I find that the more often I test my code, the better it comes out.
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?