Friday, June 5, 2009

Code Quality Improvements

The Intensity Engine's stability has significantly improved over the past two weeks. In fact, it's been a few days since I ran into a bug. But, the bugs you don't know about are just as bad, so I spent a few days doing various code quality procedures, including running valgrind and doing more serious cross-platform testing.

Valgrind is an excellent Linux-only tool for detecting memory errors at runtime (and a lot of other stuff). For memory errors, valgrind basically checks every memory allocation, read and write to see that it's valid. This is quite an intensive process, making the program run 10 times slower or worse, but one of the nice things with Cube 2 is that it's so fast that, actually, you can run it in valgrind at a decent speed and perform actual tests. And I'm talking about the client, not the server (which can also be done, but is a much less impressive feat).

Even so, running valgrind is a time-consuming task, as it takes a lot of repeat runs to get useful results (as you fine tune the 'suppressions file', the list of warnings that it can safely ignore). This took a few days, during which the following came to light:
  • Several minor issues with Cube 2 itself, including accessing uninitialized values and a mismatched new/delete. I reported them and eihrul committed appropriate fixes (which I then ported to the Intensity Engine). I am somewhat surprised none of these led to noticeable issues in practice, but I guess most compilers initialize enough stuff by default to prevent it. (Perhaps compilers should have an option to not initialize values, for testing purposes?)
  • One serious issue with Cube 2, concerning bone processing in the skeletal animation system: When bones were 'unused' (not appearing in actual blend weights, etc.), that led to a read at index -1 in a C array. As with the previous issues, I reported this and eihrul committed a one-line fix.
  • One minor issue with how the Intensity Engine uses Cube 2: I assumed a value was initialized by default, but it wasn't (not sure why it isn't, it somewhat goes against the conventions elsewhere, but regardless the fix was trivial).
  • One serious issue with, of all things, the Intensity Engine's logging system: An incorrect reuse of a function receiving a variable number of arguments by functions passing it only one (it got confused in some cases and looked for arguments that didn't exist). The fix was trivial.
This actually went better than I expected: Given the 65,000 lines of Cube 2 code, some of which I modified, +30,000 lines of Intensity Engine code that I wrote, I would have expected more issues, or at least more serious ones. Only one serious issue in each of Cube 2 and the Intensity Engine is not that bad.

As a side issue, the trickiest part with running valgrind turned out to be Google V8. Valgrind reports a lot of issues in V8, presumably because V8 dynamically generates machine code from JavaScript, and furthermore modifies that machine code on the fly. In other words, the issues valgrind finds aren't problems with V8 itself, but with the dynamically generated code. And generating an appropriate suppressions file for such stuff isn't easy (not sure it's even 100% possible).

The second thing I did was finally get around to some serious testing on Windows, as during the last few months I only found time for some partial testing myself now and then, and some very useful community contributions. So, the time was right to make sure this worked, and after several hours the client was running fine (there remains an issue with the server, something minor about how linking is done, which I'll fix later on). Interestingly, as always cross-platform testing uncovered some stuff:
  • The serious issue mentioned above with the Intensity Engine logging system led to actual crashes on Windows (while on Linux no errors occurred in practice). So, interestingly, this problem could have been discovered by either valgrind or by cross-platform testing.
  • An issue with reading files using the C API: What I wrote worked fine on Linux, but not on Windows, apparently due to underlying platform differences. The fix was trivial, and even shortened and improved the code: To use Python's file reading system, which is already tested for cross-platform compatibility.

Overall, writing in C++ definitely has its downsides, as the issues mentioned above will attest, and that's why big parts of the Intensity Engine are written in Python or JavaScript: No memory leaks (for the most part), no invalid memory accesses, fewer cross-platform compatibility issues, etc. But with game engines and virtual worlds platforms, the core speed/memory-intensive part really has no choice but to be written in C++. It's not easy nor always fun, but it is manageable.

6 comments:

  1. I just found out about your program and I wanted to know how to join the alpha beta

    ReplyDelete
  2. Hi Roland,

    Either email me (contact -at- syntensity.com), or talk to me on IRC (#intensityengine on FreeNode), and I'll give you an invitation to the alpha.

    But, I should warn you, the alpha is in an early stage. If you don't want to compile the client from source code, then you should wait a few weeks until we start supplying pre-built clients for all platforms.

    - Kripken

    ReplyDelete
  3. > But with game engines and virtual worlds platforms, the core speed/memory-intensive part really has no choice but to be written in C++.

    What about C, Objective-C or D?

    Tim Sweeney has been looking into the ML family and Haskell. As he's looking for concurrency, and is interested in pure functional languages, I'm surprised he didn't consider Erlang.

    ReplyDelete
  4. Hi amk,

    You are correct about C, Objective-C, etc. - when I said C++, I should have said "C/C++, or a language with a very similar performance profile."

    To use the terms in the interesting presentation you linked to (thanks!), I guess what I was saying is that Game Simulation might be done in a scripting language, but Numeric Computation should be done in C++ or a similarly performing language. (Purely functional languages are an interesting direction - I'll wait to see the first successful engine built on that before forming an opinion, though :)

    - Kripken

    ReplyDelete
  5. I've been reading a lot of people complaining about C++ specifically recently, such as the C++ Frequently Questioned Answers and the (out of date, e.g. it's pre-namespaces) UNIX haters handbook chapter 10. D sounds like it might be an improvement, but as far as I am aware only about three people in the world actually use it for something.

    ReplyDelete
  6. amk, those links are a bit extreme in their criticism, I'd say - C++ isn't *that* bad :) But yes, it has enough problems for me to prefer languages like Python whereever possible.

    D is nice, but I doubt it will be ready for serious use any time soon (I hope I'm wrong about that, though).

    ReplyDelete