Sunday, August 29, 2010

We're hiring

Interested in working on the J9 VM in Ottawa? We've got a job opening.

Note that a demonstrated aptitude for low-level programming is so important that we've written it twice. We've written it twice.

If you're interested (and you have a demonstrated aptitude for low-level programming) apply through the web site or send me a note.

Saturday, August 28, 2010

Debugging tip: timing holes

Parallel programming is hard. Really hard. And, as we're now firmly in the era of multi-core computing, it's an increasingly important skillset.

A couple of days ago we hit an assertion in some code. The failing test case reproduced the problem 2 out of 300 times. So it was reproducible, but highly intermittent.

The developer who was debugging the problem speculated that there might be a timing hole. There was a section of code which set two variables kind of like this:

section->hasRedObjects = true;
section->baseOfRedObjects = basePointer;

He hypothesized that if one thread ran through this code while another thread read the same fields, the second thread might see that the section has red objects, but, briefly, might think they start at NULL instead of the correct address somewhere in the middle of the section.

We used a really simple technique to prove this: make the timing hole bigger.

section->hasRedObjects = true;
usleep(10000); // sleep for 10 ms
section->baseOfRedObjects = basePointer;

Adding a 10ms sleep between the two assignments caused the problem to occur nearly every time, confirming his hypothesis.

(We fixed the problem by getting rid of the bool hasRedObjects field. We defined a new function, bool hasRedObjects() { return NULL != baseOfRedObjects; }. With only one field, the updates are atomic and the timing hole is eliminated.)

Monday, August 23, 2010

What's in a name?

When I first started at OTI, and as we were slowly digested into IBM, our team didn't have any formal coding guidelines. We were a small group, and everyone basically followed the same unwritten conventions and tried to keep things sensible.

As we grew it became apparent that this wasn't going to scale. So a few years ago we wrote down our coding guidelines for C and C++. (We write Smalltalk, too, but we've never had to codify the Smalltalk conventions. We do a lot less Smalltalk now, so we probably never will.) Most of them are fairly straightforward, but one overarching theme is simplicity, simplicity, simplicity.

I believe that reading code is significantly harder than writing code. (I think the opposite is true of prose.) When you're writing a new function you've already worked out the structure in your head (hopefully) and you just need to translate that into something a compiler can understand. But when you're reading code you've got to reverse-engineer the structure from the simple instructions to the compiler. Plus there's a multiplier: on any large project you'll probably come back to debug and read certain pieces of code dozens or hundreds of times, but (hopefully) you only need to write it once. So not only is reading harder, but you'll do it much more frequently.

One of our most important guidelines, and one which seems to be fairly unusual, is that variable names need to be spelled out. Don't use idx when index will do. Don't use firstOpt where firstOption (or firstOptimization?) will do.

It seems fairly minor when you're writing the code, but it's only a few extra keystrokes and it makes your code that much easier to decipher later on. Why waste a few brain cycles (regardless how minor) to expand objcnt, when you could have just written objectCount in the first place? (Or object_count if you don't like CamelCase.)

(Of course there are few exceptions. You can use i in a for loop, since it's such a common idiom. You can use abbreviations if they're at least as commonly used in speech as their expansions.)

So why is this so hard for new team members to follow sometimes? I think it largely comes down to bad examples, and there are a few reasons for this.

Computer science developed largely out of mathematics. Mathematicians love using short names. First they go through the Latin alphabet, then they capitalize all the letters, then they start stealing letters from other alphabets, and finally, like Prince, they just start making up new symbols. If they simply can't find a symbol for some concept, they might condescend to string together two or maybe three letters, but anything more than that seems to make them uncomfortable. But even very complex theorems are unlikely to use more than a few dozen variables and constants, and they've got a few hundred years of precedent, so they can get away with it.

Other sources of bad abbreviation precedents, I think, are academic papers. Because of the standard two column layout used in most journals, code examples must fit in narrow columns. I counted 36 characters in the examples here. Don't they know that impressionable young students read these things?

Finally, remember that you're not the only one who needs to read your code. Once you've moved on to newer and cooler projects someone else might have to read and maintain that code. So be polite and make their job a little bit easier -- please don't abbreviate your variable names!

Saturday, August 14, 2010

Picket fence comments

Here's a stupid trick for C++ or C99 programmers. Make your comments look like a picket fence:

// Here's the first line of my comment \\
\\ Here's the second line              //
// You can go on and on like this      \\
\\ but you have to be careful about    //
// how you end the comment, or the     \\
\\ next line might be commented out    //

Why does this work? You can't use \\ as a single line comment, can you?

Hint; this doesn't work:

// This is a comment
\\ But this isn't

Why can't you do this in C89? Because // comments weren't supported until the 1999 revision of the ISO C spec although a number of compilers "embraced and extended" the standard (I'm looking at you Microsoft and GNU).

Why would you do this? I guess you could use it to help document your Obfuscated C Code Contest entry. But in the end this is just another example of something you can do in C but shouldn't.

Sunday, August 8, 2010

What's the return value of memset?

I figured I'd start off with something I noticed last week for the first time: memset has a return value!

Every C programmer should be familiar with memset. You've probably used it like this:

void *memory = malloc(100);
if (NULL != memory) {
  memset(memory, 0, 100);

(You did test that malloc succeeded, right?)

I looked up memset last week, and was surprised to see that the man page said it returns void*. I'd always assumed that it was a void function. It turns out that memset returns the first argument.

So you could actually write the above code like this:

void *memory = malloc(100);
if (NULL != memory) {
  doSomething(memset(memory, 0, 100));

Why would you do this? Either you're running low on your budget for lines of code, or you like to confuse your collaborators.

So why does memset have a return value when it doesn't need one? I don't know, but my guess is that it's a historic artifact. In some cases it could let you avoid creating a temporary variable to store the pointer. Temporary variables take up space in the stack frame, but return values are usually passed around in registers. In the era before optimizing compilers, that mattered. But a modern compiler will remove temporary variables (or add them) all by itself -- it doesn't need a micro-optimizing programmer to tell it how to do that.