OJ’s rants What would OJ do?

10Dec/0728

The Magic of Unity Builds

I realise that as time goes by, people are using my beloved C++ less and less. .NET (C# and VB.NET) and Java seem to be taking over the mainstream coding world. Languages such as Ruby and Python seem to be taking over the scripting world. For the most part, C and C++ seem to exist only in the gaming/entertainment, real-time and driver worlds.

In many colleges and univerties C++ is no longer taught as a core subject (along with Assembly language) which I find quite galling. It's a great language to learn, even if you never use it again. But the purpose of this post is not to preach the virtues and failures of the C++ language, but instead to talk about something that might aid those people who are using C++.

Anyone who's worked on a large C++ (or C) application has felt the pain of long build times. The first "big" C++ system that I worked on took 10 minutes to build. This was much bigger than anything I had experienced up until that point. Later in my career when I joined the games industry I really felt the pain of rebuilding a massive code base. The game took just under an hour to build from start to finish without any distributed compiling mechanism in place. When I finally got a copy of Incredibuild the build time dropped noticably, but not to a point where recompilation was an option every time I wanted it.

The solution that we implement: Unity builds (which I'll refer to as UB from now on because I'm lazy).

Now, a lot of C and C++ that have used these before might consider the idea of a UB to be a bit of a hack. I'm not sure if I agree or disagree. There is an air of "hackyness" about it, but it solves the problem of huge compile times on most platforms with most compilers.

Before I go into the definition, and give instructions on how to set up a UB, let me just state for the record that this isn't designed to replace normal builds for releasing code. The idea behind these is to dramatically reduce the build times for developers who are spending 8 hours a day modifying the code. Fixing bugs and adding enhancements generally means that a developer is constantly recompiling. Every time you compile, you have to wait (or perhaps entertain yourself in some way), and in waiting you are wasting precious time. Wasting time means a reduction in productivity. This can only be a bad thing. It's bad enough with short build times, but with long build times it becomes a huge problem. So please bear in mind that the normal release and product builds, builds that happen on automated build servers, etc, do not need to have a UB in place, they can continue to use normal builds.

The principle behind a UB is quite simple. It's all about reducing...

  1. ... the number of times a file is opened.
  2. ... the number of files that are opened.

In esssence, we're going to abuse the power of the preprocessor to do the above.

Every source file (.c, .cxx, .cc, .cpp, etc) that is compiled results in the creation of an object file. When all of the source files are compiled into object files, the linker then collects up all these object files and links them together into an executable application. For those of you who don't know: reading/writing to/from disk is the slowest thing you can do on a computer. Actually, it isn't so much the reading and writing, it's more the seeking. But the point remains the same. Disks are the only core parts of your system that have moving parts, so they're going to be slow! So if you want to speed things up, a good place to start is reducing I/O.

I don't want to turn this into a lesson on how C and C++ programs are compiled, so I'm going to cut to the chase. We know that the preprocessor parses each file before it's compiled which means that each file is going to be opened. When those parsed source files are then compiled, they're essentially opened again. Each of those files requires another seek. When the object file is generated, that's another seek. When the file is linked, that's another seek. There's lots of disk activity going on here!

If you haven't caught on yet, let me give you another hint. Each source file results in a couple of new files and a stack of seeks. We also have a neat preprocessor statement at our disposal (*cough* include *cough*).

That's right guys, we don't actually compile every file individually. We instead create a master file (or set of master files, depending on how big your source base is), which includes all of the other source files. This file (or set of files) is compiled instead.

Sample ProjectTo make it clear I'm going to show you the principle in action on a fake project in Visual Studio 2008. However, the principle can be applied regardless of the compiler and platform. Check out the screenshot on the left. This shows a standard C++ with a few files in it. Ordinarily each of those files will be compiled individually, and hence slowly.

The first step in creating a UB is to make a new project configuration which you can play with. Doing so will allow you to set up an area you can modify without breaking the "normal" project which will continue to be built as usual. So let's do that now. Create a new project configuration based on the debug build. See the following screenies:
Configuration Manager - New Config
New Solution Configuration
Configuration Manager - Config Added and Selected

Build Toolbar - Config SelectedWhen you've created the new configuration, make sure you have it set as the active configuration as shown on the right.

Solution with Unity Build FileThen, you need to add a new file which will be the master UB file. I usually create a sub-folder in the project and add the file there. Both folder and file I tend to call UnityBuild.

Unity Build File ContentsWhen the file is created, the first thing we need to do is make sure that we edit the UnityBuild.cpp so that it includes all of the other files in the project. Check out the image for the example.

If we attempted to compile at this point, we have all kinds of issues. Everything right now is being included twice. So we need to fix that so that we don't have issues in every configuration. Let's start by fixing up the Unity build project. Select all of the source files except UnityBuild.cpp, and exclude them from the project (right-click on them to see the properties). Check out the pics below to see how it's done.
Files selected
Exclude from build
Solution with files excluded

So that this point you've excluded the files from the UnityDebug configuration, and hence the unity configuration should build.

Note: if you're implementing this in a large existing source base you may find that the UB doesn't actually compile. You need to bear in mind that the preprocessor is making one large file out of all of your source files. Which means global variables, static variables, global functions, etc that appear more than once in the source code will now tread all over each other. Technically you shouldn't have this problem if you're writing "proper" code :) But the harsh reality is that lots of devs copy and paste code, duplicate function names, and all kinds of other horrible things. To get it working, you're going to have to do a bit of housekeeping! But since this is going to affect the source in a positive way, it can only be a good thing!

Unity build file excludedOK, next up let's get the other builds working again. You need to exclude the UnityBuild.cpp file from the build in both the Debug and Release builds (not the UnityDebug configuration). I won't show you how to do that, as it should be obvious. When you're done, you should see similar to the image on the left when you use the Debug or Release configurations from this point on.

You're done! The old builds should work just fine because your UB file isn't included in the configuration. Once you've sorted out the code duplication issues with the UB configuration, you should have a perfectly working configuration.

You may find that the whole process is much easier if you're building from the command line, especially using make on *nix machines, as you don't have to worry about excluding things from configurations. Instead, you just simply need to pass in a single file to the compiler - the UB file!

So after all that, what kind of improvements should expect to find? Well let me give you a few stats. When the UB was implemented at the game company I worked for, the build time dropped from 55 minutes to just over 6 minutes. When I implemented UB in a previous job the build time dropped from 10 minutes to less than 3 minutes. When I put them in place at home the build times dropped on average by 60%.

I'd be very surprised if you didn't notice a huge improvement in your build times if you use this mechanism.

Before I finish up, I need to highlight a few issues around housekeeping. As already stated, these builds are not used to replace the normal compilations. They should sit alongside to make it faster for the developer to do his or her work. This means that you need to make sure that both types of builds are working all the time. Keep your automatic builds using the normal build type, and you'll soon find out if you've not been maintaining your configurations properly.

Make sure that every time you add a new source file you exclude it from the UB configuration, and that you add it to the content of the UnityBuild.cpp file otherwise it won't be compiled and included in the build properly.

On the whole, UBs have really improved the speed in which I've been able to do things. My wait time for compilations is generally a third of what it was. That, in my view, is worth the "hack" :)

Thanks for reading!

PS. Please let me know of any typos, grammar errors, etc. It was a hefty post to put together and I might have missed a couple of obvious stuff-ups!

EDIT: I have fixed up a few grammar and spelling errors. Thank you Yoann and Bryce :)


This post is a bit old, and some of the images are missing. While it's still got a valid description of Unity Builds it's not really complete without the piccies. So I've created a full screencast and posted that over here to clear up any confusion. Cheers!

  • OJ
    Yeah, they have been for quite a while after a server fail and a lack of backups from a previous host. I haven't gotten round to recreating them yet. Sorry.

    Check out the screencast, that should still work fine.
  • mofle
    img-links are b0rken :/
  • OJ

    @Rosie: I remember you :-) When I first saw this comment I had to think about it a while before I narrowed it down. Earlier today I spoke to Mr Jose to confirm it was indeed you. It's nice to hear from you after all this time!


    I appreciate your comments mate. I'm a passionate little geek and it's great to hear that other people feel it too.


    Thanks so much for posting your comment. That's indeed a pitfall that can bite anyone if they're not careful.


    Hope all is well back in the UK!

  • Rosie
    I remember you well from your UB job #2,  and how refreshing it was to hear the way you talk about code,  its good to see you're doing so well and sharing the joy.
    Ive been doing this trick for a while essentially out of laziness while building with gcc/mingw , notepad++ and a .bat file on a mini laptop, using the unity.cpp like a makelist, but I recently hit a numpty problem which I thought was worth adding to this blog. It could help someone who may be frazzled from chasing bizarre memory problems like ive been recently.
    Its an obvious thing, but Id forgotten it.  By making changes in a source file that is part of the ub,  the compiler doesnt know youve made the change and is using the old unity.o file, so a full rebuild is needed. I never had this problem before because I used to only tidy away fully completed work into a unity file. - Now Im reorganising my files and everything was slowly breaking.
    On any other setup I've used previously I would have used the 'make clean' button or script long ago and realised what was wrong, but I didnt spot it until I noticed that gcc wasnt complaining about undefined references to new test functions which I hadnt yet declared. 
    - It 's not a downside, but fools should beware this method is not foolproof!
  • OJ
    @Protector one: No need for thanks! :) I try to make a point of replying to anyone who makes the effort to comment on my blog, especially if they're asking questions!

    Good luck with the Unity Build. Keep us posted on your findings. I'd be interested to see if they live up to expectations (that is if you have any ;) )

    Cheers!
  • Protector one
    Thanks for replying at all. Unity builds it is, then. :)
  • OJ
    @Protector one: Tough question to answer imho. There are so many variables in a given project it'd be very hard to determine when best to use them and when not to.

    I'm in the same boat as Jon. I don't use anything other than unity builds for all the reasons that we've discussed.

    Right now I can't really come up with a good reason for not using them :) (particularly on new projects).

    PS. Sorry for the late response. Been hard to get time online of late.
  • Protector one
    I'm a little late to the party, seeing as how the last comment is from June, but hopefully someone will know an answer to Jon's hypothetical question: "...seeing as the unity build is not only faster to build, but also generates as good or better code, at this point, you have to start wondering what the point of the non-unity build is any more[?]"
    So, is there any reason to have a non-unity build? Or somewhat equivalently, what are the downsides of unity builds?
  • OJ
    Hi Swallow, drop me a line via the contact form and we'll discuss it offline. Cheers!
  • swallow
    Hi, oj

    I heard UB other site, and I found your godd article.

    If you don't mind, I want this article translation to korean for my site. :)
  • OJ
    Yup, you're dead right Iain. I should have made that clearer :) Cheers!
  • IainB
    I think it's worth noting that while full links are much quicker, incremental links are much slower, as it's having to link in more stuff.
  • OJ
    Hi Code Tortoise,

    Yes it most definitely does have a positive effect on link times for the same reason it improves compile times. Instead of linking many files it only links a few (or even one!). The link time drops dramatically.

    Give it a shot :)
    Hope that helps!
  • CodeTortoise
    Does a unity build have a positive effect on link times?
  • Mr. Smith
    Sounds great, OJ. Thanks again for all your help - it's appreciated. Good luck with your son :)
  • OJ
    Mr Smith,

    I haven't had the chance to do it tonight, but I'll aim to have it up for you Monday your time. Had a bitch of a day dealing with my son ;)

    I've decided I'm going to add not just the screenshots (which my server seems to have gobbled), but a screencast on how to do it, along with a C++ project (including a chunk of source code) demonstrating the speed up.

    That should keep the masses happy :) So watch this space! I'll have it up shortly.
  • Mr. Smith
    Thanks, OJ! I'm very much looking forward to seeing those shots. Please take your time, it's Friday night here on the east coast of the US, and I won't be working on this again until Monday.

    Thanks again for sharing!
    Cheers,
    Mr. Smith
  • OJ
    Hi Mr Smith!

    I had totally forgotten about this! I had planned to restore those pics ages ago but it slipped my mind. Sorry for the tardiness. I'll aim to have them back up online by the end of the day (Australian time ;)) and I'll drop you an email when they're up.

    Cheers mate!
  • Mr. Smith
    Hi OJ,
    Thanks for posting this guide. I've been on projects that have used them before, but I never really paid attention to how it worked. Now I need to set this up for a very large project I'm having to frequently build. I'd be very happy to see the screen shots restored to show exactly how you're doing this. Thanks
  • OJ
    Hi again Tim!

    Thanks again for the feedback. I don't blame you for holding back on where you work, it's best to keep that kind of information to yourself. I'm very happy to hear that your company has benefitted from this technique as muchj as you have personally. That's great news. It's always good to know when your efforts help others!

    Hrm, I have just noticed that the screenshots have just disappeared off the server, I don't know what's going on there. I'll see if I can dig them up again.

    Thanks again!
  • My pleasure :) We now use Unity builds at work and it's improved productivity considerably. I'm a bit reluctant to say who I work for on line (I can never quite recall what my contract said about that sort of thing) but I can tell you that a number of high profile game projects know of your site and are very grateful for your pointing this technique out.

    Tim.
  • OJ
    Hey Tim!

    Wow :) What a fantastic comment! I'm so happy that you've felt some of the benefits of Unity Builds already. Isn't it amazing how quick they can be! And you're right, the time you spent waiting in the past is time that you're never going to get back. Shame it takes us so long to catch on to stuff like this eh? When I learned about them in '05 I was cursing myself for not knowing earlier.

    Your feedback is greatly appreciated, I'm really chuffed to hear that you have benefitted from the post. Cheers mate and all the best!
  • Thanks so much for this, if I'd known about this 5 years ago then I'd have saved so much of my actual life it doesn't bear thinking about. I mean, all that time wasted waiting for code to compile; you're not going to get it back.
    This actually outperforms incredibuild in some circumstances - combining it with incredibuild gives the sort of compile times I used to enjoy using Borland Pascal.
    This is particularly helpful if you've got a lot of one class = one header source in your code base. So, essentially, making your code nice and readable makes it compile slower.
    The one thing that amazes me is how microsoft and other compiler makers don't implement this sort of thing internally? Surely they must have thought of caching files in memory. Programmer man hours are too expensive to be waiting around for the stupid compiler to grind it way through files.
    I'm sure many projects that have failed may have had more positive outcomes if there had been a quicker compile turn around. Some projects I've worked on could take over an hour to totally rebuild and that really, really adds up when you're fixing things that get included everywhere.
    Anyway, thanks buddy, you're a life/project saver.
  • OJ
    Of course I want opinions ;) Otherwise I wouldn't be blogging at all! This isn't about preaching, it's about learning, and I can't learn unless people comment.

    Thanks for the feedback! I hadn't really considered the optimisation side, for me it was more to do with reducing time and increasing productivity. I also like the fact that your code has to be well written for unity builds to work (ie. you have to remove a lot of dirty code if you want it to build).

    But now that you've mentioned it I might just do a bit more reading.

    Cheers!
  • Jon
    If you want my opinion (and if you don't, then I question the point of having a comments section), you missed one thing. If you're using a unity build system, because you're handing the compiler huge chunks of code, you're giving the optimizer a better chance to make your code faster, as it has a larger scope in which to operate. Now, seeing as the unity build is not only faster to build, but also generates as good or better code, at this point, you have to start wondering what the point of the non-unity build is any more (apart from just being a maintenance burden). Most of my personal projects now only use unity builds, and I have no particular desire to make them non-unity. As a parting shot, I thought you might also be interested in http://www.sqlite.org/amalgamation.html, which appears to be an independent implementation of essentially the same idea.
  • Gav
    Rather than doing the whole include/exclude from build thing - it gets rather tedious if you keep adding new files in and makes the project file rather large which makes merging it a bit of a pain - we added a separate project to our solution specifically for the unity build.

    This way we can build that project for fast build times or the normal project for slow build times and don't have to keep remembering to exclude new files.
  • OJ
    Yeah I was thinking the same mate. The issue is as things get really big then you'll end up running out of memory. But they should be smart enough to get round that problem without any interaction from the user.

    Having a different project for the unity builds is a neat idea! Thanks for that :)
  • Iain
    You'd think in the modern day and age we'd be able to cache the files in memory to avoid going to the actual hard disc everytime...

    My current project puts the Unity.cpp files into a separate vcproj file, so that you don't have to worry about the whole "exclude from build" stage that can easily trip you up, you can control that at the solution level. It also autogenerates the unity files so you don't have to remember that stage either.
blog comments powered by Disqus