Indie iPhone game development

Bad News for Scons Fans

We have been talking a lot about Scons recently at the Power of Two Games World Headquarters. MSBuild has proven to be quite a pain to work with for our asset builds and eventually left us dissatisfied (that’s material for a whole other entry). So we kept looking over to Scons as a possible solution.

On paper it looks great:

  • Built from the ground up for parallel builds
  • Uses a real programming language (Python) instead of overly-complicated XML files
  • It allows for discovery of assets to build as other assets are parsed

The big question mark hanging over it was whether it was fast enough. I had done some experiments with Scons and other build systems a few years ago, and Scons totally failed by having ridiculously long incremental build times just checking if anything had changed. That was simply not acceptable. If no data has changed, I want the build system to detect it and come back in a second or less.

Since then, I’ve been told that Scons had finally fixed its dependency checking and it was much faster now. Music to my ears!

So I resurrected the old script I wrote back then to generate a nightmare stress test, downloaded the latest stable version of Scons (1.0.1) and fired it up.

I’m sorry to report that Scons is still as unusable as it was back then. I generated a codebase like the one I used to measure all the build systems (50 libraries, 100 classes each, 15 internal includes, 5 external includes). I built everything once, then ran it again and it took 49 seconds. I have some pretty nice hardware, including a fast SATA hard drive and a 2.8GHz Core2Duo with plenty of RAM, and 49 seconds to say that nothing needs to be changed just doesn’t cut it.

Now to be fair, right now we’re looking at Scons to build our assets, not our code. Maybe the nested project configuration is not particularly realistic, so I tried something simpler: 5000 files in a single library without any dependencies among each other at all. This is a very conservative example for an asset build of a large commercial game.

  • It’s only 5000 files. A real game can easily have 10 times as many asset files.
  • All the files are tiny (cpp files in this case). Assets can get very large when dealing with textures, sounds, and animations.
  • There are zero dependencies among assets in this example. You’ll often have models depending on shaders and textures, levels on game entity definitions, etc.

Building assets doesn’t have to be the instant, no delay kind of build that I require building code. I expect most developers will be building assets locally, tweaking things, rebuilding, and trying them in the game. The faster the build is, the better, but a delay of a few seconds can be acceptable. If Scons can’t handle this build in less than 10 seconds, it’s certainly doomed in a full-scale game.

The result: 37 seconds!

For purely masochistic reasons, I decided to see how it scales, so I threw 20,000 files at it (again, without any dependencies). Incremental build time without any changes: 3 minutes and 46 seconds. That’s over 6 times longer for processing 4 times the number of files, so it doesn’t even scale linearly.

Scons is definitely too slow, and MSBuild has its share of problems. What’s left out there? Any recommendations for good parallel asset building systems? Hopefully something lightweight and easily configurable through a real language. Anything?

  • Digg
  • del.icio.us
  • Facebook
  • Reddit
  • StumbleUpon
  • email
  • PDF

Related posts:

  1. The Quest for the Perfect Build System
  2. The Quest for the Perfect Build System (Part 2)

Post Metadata

Date
September 18th, 2008

Author
Noel

Category



28 Comments


  1. Edd

    I haven’t got around to playing with these tools yet, but perhaps you might find some time if your get desperate enough?!

    http://sham.sourceforge.net/
    http://www.cs.berkeley.edu/~billm/memoize.html

    They basically work off of the same idea: run the commands to do the build, monitoring the inputs and outputs with kernel hooks to generate dependencies information. No need to specify or calculate the dependencies explicitly via some heuristic hack.

    I imagine it would be pretty easy to implement a parallel build system on top of these. You could simply write your build instructions sequentially in a list with certain barrier points. All commands between barrier N and N+1 could be run in parallel. e.g.

    compile blah/foo.cpp blah/out/foo.obj
    compile blah/bar.cpp blah/out/bar.obj
    compile blah/baz.cpp blah/out/baz.obj
    compile guff/foo.cpp guff/out/foo.obj
    compile guff/bar.cpp guff/out/bar.obj
    compile guff/baz.cpp guff/out/baz.obj
    ——————————————- # barrier
    make-library guff/out/*.obj libs/guff.lib
    make-library blah/out/*.obj libs/blah.lib
    ——————————————- # barrier
    # etc …

  2. Could you make the generator for your test code available, or email it to me? I’m guessing it’s C or C++, so the number of preprocessor defines is important too.

    “50 libraries, 100 classes each, 15 internal includes, 5 external includes). I built everything once, then ran it again and it took 49 seconds.”

    I’ve done a fair amount of work recently analyzing no-change (aka null, do nothing etc) build times with SCons.

  3. I *HATE* (capital H) scons.

    My current project and my last project used it. The #1 issue I have with it is no one seems to actually understand it and so because it’s just a python library and you have all of python with which to hang yourself every sub project ends up being some personal hack of the guy who setup the build for that sub project. Back in make days, editing or adding something to a makefile was always relatively easy. Writing my own build system in perl or python has been relatively easy but everytime I see someone go try to add something to scons it’s 4-8 hours of pain.

    One thing that many build systems seem to have problems with is dependencies that can’t be known before the build starts. An example is you don’t know what textures need to be processed until you build the maya/max file. So what you really want is

    .mb->.middleformat->.texturelist->[texture1, texture2, ...]

    When .texturelist already exists and is up to date (ie, newer than both .middleformat and .mb) it’s easy for some build system to follow the rules and build the textures but most build systems I’ve used don’t like it when that list doesn’t exist yet and therefore has to be added after the build has started. Or to put it another way, most build systems start with only some programming language in mind and make the assumuption they can scan the text quickly to figure out implicit dependencies. That assumption is not true for assets.

  4. ^_^

    Waf.

    It has evolved from Scons years ago to address several flexibility concerns and performance issues. Have a look:

    http://code.google.com/p/waf
    http://freehackers.org/~tnagy/testdoc/index.html

  5. Chris Williams

    My team recently migrated from the Autotools suite (*that’s* pain) to SCons. Our codebase isn’t that large (~175 files code, more for other stuff (Doxygen etc.)), but we found Scons was, while slower than ‘make’, easily fast enough for the ease of use difference.

    I just has a quick look at your test script, and I notice it’s not using any of SCons’ speed features. Reading your problem domain, one in particular might be useful. SCons uses objects called ‘Deciders’ to figure out if a file/dependency has changed. The default Decider uses MD5 sums of the file contents, which adds a *lot* of time overhead. The reason this is the default behaviour is explained here: http://www.scons.org/doc/production/HTML/scons-user.html#AEN815, but the important point is that it can be easily changed (using one of several built-ins or writing your own). There’s one that will use the later-than-mtime instead, like make, (flaw: doesn’t catch introduction of older files into the file tree), and another that’ll use mtime-exact (any different mtime causes a rebuild), and several more. There are trade-offs involved with these, but we found that mtime-exact took 66% of the time of md5s, which is not as fast as others but takes a lot of the sting out.

  6. Michael Chiu

    Is kjam still alive ? I have sent mail to him but no response.

  7. noel

    Frankly, I had never really considered test-driven builds. We’re definitely test-driving our data conversion tools (take this model from the intermediate format to the final one).

    The main problems I see with a test-driven tool is that the high-level build process is a well-understood, generic process. It’s also a process that is highly performance sensitive and it’s going to be stressed with Gigabytes of data, so you want to parallelize it as much as possible. So given that, it seems that trying to adopt someone else’s solution is the way to go. Yes, there’s still no perfect build system out there, but they’re probably better than what a single person can do in a month or so.

    On the other hand, if your game doesn’t have too much data, then you can just grab any of them (make, Scons, MSBuild) and be happy with it and you can spend your efforts in the things that really matter (the game). In general I’m not a fan of writing build systems from scratch as a set of custom scripts though.

     

  8. Bob Lauer

    Noel, what’s your take on test-driven builds? I.e., don’t use generic build tools
    but rather write a test that formalizes the requirement “I want to generate
    data format xyz from source format abc.” This test can be tuned as much as necessary,
    e.g. it could be rewritten as “Check for the abc file with the latest date. Read
    the latest date of the target format’s file (from a cache, not considering every file).
    Run task if required.”

    The bad news: It’s more work.
    The good news: It allows you to tune the build as much as necessary. Actually, you may only
    have to tune
    some parts of the build. The language doesn’t really matter, any generic scripting
    language will do (say Python, or Groovy with its nice integration of Ant tasks).

  9. Tom Johnstone

    Can anyone tell me what kind of license/cost KJam is?

  10. Alex Podverbny

    That’s great.

    Not that I need anything added or fixed in Kjam, but it’s good to know the tool is ‘alive’ :)

  11. Robin Green

    Jam, the unsupported devil child of Perforce, may just be the ticket. Many people swear by the Boost version of Jam, I found the “ftjam” version easier to build and it takes the “precompiled header” extensions quite easily.

    http://freetype.sourceforge.net/jam/index.html

    Stephen Knight, creator of Scons, sits upstairs and works on building, among other things, Chrome. And yes, it sucks, but our previous system sucked more and knew nothing about being cross platform.

  12. Kurt

    This page

    http://www.scons.org/wiki/GoFastButton

    says

    The command ’scons –max-drift=1 –implicit-deps-unchanged’
    will execute your build as fast as possible.

  13. We’re using SCons for several years now to do all our asset builds, some code builds (lua wrapping classes), vcproj generation, test building, etc.

    It’s horrible.

    For one thing, it’s very slow;
    Then, there is a constant stream of build-related problems every week (though perhaps we’re doing something wrong here).

    Also SCons (as it seems) has a bug with complex dependency checking scheme – i.e. we have a .ma -> .dae -> binary file geometry pipeline, .dae file is scanned for textures, which are built before binary file, so binary file depends on them; if .ma has changed, then in some cases the dependency tree contains BOTH dependencies from old .dae file AND from new one (just generated).

    And the best thing of course is that we’re not going to change it.

  14. AdrianNeagu

    So you have .ma -> .dae -> bin. The specific aspect is that you have
    a derived file with many implicit dependencies.
    Bad news for you: this is a demanding file flow that all build systems
    have and will always have a very hard time to deal with.
    Some systems, make for example, can even hardly do a correct build
    for such flow in only one pass (usually they do it in 2 passes).
    AT the other extreme are systems that do it correctly in one pass
    but in a prohibitive time because they read and scan those derived
    files with implicit dependencies on every null build.
    What you should know is that the task
    of making such a build both fast and safe is way more complex
    problem than most of us like to believe it is.

    Even more bad news for you, SCons is one of the best at managing
    such builds, from the corectness point of view and not only.
    The other tools that I know fare much worse. For example, SCons
    gives you the choice of caching implicit dependency info, caching some
    or not caching at all. You probably rushed to use that –implicit-deps-unchanged
    because it is displayed on every wall without considering carefully if
    your build is in one of those corner cases that cannot afford that optimization
    or not. In your particular case, you complain about some extra dependencies
    and low speed. You should ask the question if checking those extra files
    is faster or not compared to updating the the list of dependencies. You may get
    a surprise…

    I do know that most of us are in a position where we cannot change
    the file flow to make it more build-friendly. The price to pay
    is quite often longer time spent in builds (multiple passes, time to scan
    files with information that in fact belongs to the build description, etc).

    If you spot a good build system for a build like yours, I’m highly
    interested. Do tell.
    Adrian

  15. Hi,

    There are two viable alternatives to scons.

    The first one is waf. It was written after attempts to convert the KDE project’s build system to scons. After a while, Thomas Nagy found too many limitations in scons (including but not limited to the one you witnessed) and wrote his own build framework in python. It’s used by several open source projects but is not really mainstream.

    The other alternative is September 19, 2008 at 5:24 am

  • thothonegan

    I also agree with CMake. I haven’t had to use it for any sort of resource handling, however it works very well for building complex projects, and the IDE independence is really nice if your working on multiple platforms, or if you decide you don’t want to use an IDE for the day :P .

  • At least it’s in all the cases I looked at. Windows/NTFS manages a paltry 400 queries/second on a cold cache. If it feels up to the task that particular day – performance is highly unpredictable. That means just over 12 seconds only for looking at the file times. If you have to actually open any files, your performance is completely ruined.

    In other words, one second for the null-build is not achievable. At least not on a vanilla NTFS system. You can speed it up somewhat by doing your own dependency checking, and checking directory modification times first, traversing the NTFS changelog, or constantly monitoring the filesystem for changes and issue queries against that change cache.

    I suggest investigating different file systems, or, if opening files is your issue, investigating SSD drives.

  • Alex Podverbny

    From my experience, kjam is most suited for gamedev building tasks. It’s much faster than make (GNU make) and original jam.

    For my current project at work (8 libs of 10..50 files + app of 200 files + >5000 resource files) it takes ~2..3 secs to check all the dependencies (if I hit build button and it’s nothing to do actually).

    The main and only problem with kjam is its unclear future — last update was about year ago.

  • Mark Allender

    After emailing, it looks like KJam is still being supported. I heard from the developer this morning. He just hasn’t updated his website.

  • Mark Allender

    We have just started to investigate the same build system issues. In fact, I’ve spent the day trying to recreate some of Noel’s tests with VS2005, vcbuild, msbuild, Jam, FTJam, KJam, and Scons. I had found the same slowness with Scons that Neil has just posted about — it’s too slow.

    In response to a couple of other comments. While the filesystem might be slow, other build systems have clearly figured out a way to do faster dependency checking. Maybe they are better at caching dependency information, who knows. But when you run the same tests with different build systems on the same (slow) hardware and some of those build systems can do their incremental builds an order of magnitude faster, I don’t see how the filesystem to blame. Maybe Scons is “perfect” in it’s dependency checking, but maybe they’ve gone overboard.

    For kicks, I did try the –max-drift and –implicit-deps-unchanged and it cut Scons build times down from around 50 seconds to 35 (in my case, which is pretty similar to what Noel used I think). Still, too slow.

    Unfortunately, I haven’t found a better solution. Perhaps cmake is it. KJam looks interesting but as another poster said, they haven’t updated their site in 18+ months :-(

  • As a long time SCons user and promotor, I would like to:
    1) give some explanation on why SCons is slow
    2) comment on the goals of your quest for a good build system
    3) give some suggestions on candidates to investigate

    1) True, the null build is slow and true, the file system speed
    cannot explain it all. One root cause is low speed of computing
    signatures which involves a lot of string interpolation.
    For example, the command lines (among others) are part of
    the signature of the built files and the command lines are made
    up with Perl-ish substitutions, like the C compilation command line
    is the result of “$CC $CCFLAGS -c $TARGET ….” expansion.
    That is both good and bad. The bad part, you already mentioned it:
    it is slow. Moving character strings in memory is generally seen
    as fast but when you have many, many such operations the time adds up
    to something large in the end.
    The good part is two fold. First, the command line part of the signature
    makes SCons one of the safest build systems around. Jam and brothers
    cannot come anywhere close (without a lot of glue or other external help).
    Second, that Perlish way to describe command lines is considered
    friendly by many. Of course, the casual builder couldn’t care less
    but they are not the only stakeholders of a software build. Some people
    are prepared to spend a more time building for less time writing/developing
    the build description.

    2) Faster is less safe and safer is less speedy. That’s so fundamental that
    your desire to get to a eye blink null build will not take you anywhere.
    It easy to get a system that checks less and is faster (say, gnu make, non-recursive)
    or a system that checks a lot and is slow (SCons is the best example there).
    A smarter system would allow you to decide per run and per target
    how to compromise between speed and reliability. SCons allows that
    but to an extent far from getting rid of the sloweness reputation.
    To my knowledge, there is no opne source product to be at the same time
    safe, fast and extension-friendly.

    3) In your case, you have quite some build steps that are not
    code compilation but data compilation. That means you have
    (or need to write) your own build steps. Any advanced feature
    related to C/C++ build is not very helpful in that case.
    A lot of the tools that have been recommended to you are
    de facto C/C++ build tools (me, I was a speech scientist busy
    with statistical modeling, data compilation by excellence).
    So my advice is two fold. First, try waf (will be faster than SCons),
    try rake (may be slower, not sure). Second, be prepared to develop
    the build (Most SCons enterprise users, have their own
    wrapper for SCons). Since safe+fast+extension-friendly is not
    yet readily available as FOSS, you will have to spend
    some development time to get close. Or wait a bit more for your builds.
    Up to you to make a balance.

    Feedback welcome,
    Adrian.

  • I’m sorry that I recommended to you a try out of rake
    when you already looked at rake and rant many years ago.
    I was no yet aware of your paper here:
    http://www.gamesfromwithin.com/articles/0509/000100.html

  • Dude, MSBuild blows scons out of the water for runtime, and it’s every bit as flexible. We have our entire asset pipeline built around it, and the _deep_ asset dependency scanning for our project is on the order of ~100ms total, and that number has been more or less stable as we’ve added more assets. We build a ‘level’ target, and we have custom tasks, scanners, and emitters (to use SCons parlance) that crack the level file, figure out which game entities were placed in it, cracks the game entities to figure out which models they use, cracks the models to figure out which textures to use, and adds them all to the build set. It has great diagnostics and does correct dependency scanning orders of magnitude more quickly than SCons. MSBuild is emissive, programmable, does everything correctly and quickly, and is as far as i can tell equal or superior to scons in every way….

    except parallel builds. MSBuild doesn’t go wide easily at all, and it requires a 90-degree rotation of your data dependency expressions, which is its fatal flaw. SCons does this very well, and I’m pretty disappointed in the MSBuild team’s decision to go parallel on child MSBuild tasks instead of integrating concurrency natively and deeply with the known build dependency graph.

    You’re so SCons-centric that you’ve accepted its dichotomy of speed vs. correctness. While that dichotomy may be true in SCons, it’s not true globally and that’s why we’re up in arms to find something that doesn’t suck. Jam and MSBuild are _significantly_ faster than SCons during the dependency analysis phase of identical projects. This isn’t because they’re incorrect, it’s because THEY DON’T SUCK. I agree that SCons is correct, and good for it. I need something correct _and_ fast, though, something that doesn’t take 30 seconds to tell me that there’s nothing to do.

  • AdrianNeagu

    Thank you for the info. I’ll go study MSBuild (didn’t look at it for more than 3 years, I was so disappointed back then) for the sake of this extensibility that you mention.

    I surely believe you that is much faster. Unfortunately for our developers, we
    give up the speed if, for example, we cannot reuse it on Unix build machines.

    Thanks,
    Adrian.

  • Alex podverbny

    Why should such dependencies be implicit and why should it be a problem?

    We actually do have dependencies like .dae -> .mesh -> .pak (the same for animations, textures and lots of other stuff). We do NOT have problems with it — if some .DAEs are changed then build-system re-build mesh, re-pack package and re-build game .ISO (if I need it). The process is quite fast.

    There is ony one situation in build-process that can cause real problem : one binary depends on second binary and you cannot detect such dependency without reading _content_ of this binary — source files can be easily scanned for ‘#includes’ (and some build-systems do it well), but binaries are harder to deal with.

  • Mark Allender

    Charles — can you expound a little on what you mean by “90-degree rotation of your data dependency expressions”? I’m not quite sure what you mean by that. As I stated earlier, I’m also trying to find some build system (mainly for data, but one that supports xplatform code and data would be the best). Msbuild is on the list, but I have my own cons on that particular app. Was curious about your comments about the data building aspect.

    thanks.