Voodoo and the register keyword

When the C programming language was born, the language was very close to the machine. But both compiler technology and computer architecture have moved on since the invention of C in the early 1970s, and modern optimizing compilers sometimes translate in mysterious ways. Writing high-performance code now can be as much about exposing opportunities for optimization and letting the compiler do its job as it is about micromanaging the details of what the compiler should do. Human beings are better than compilers at seeing high-level aspects of an algorithm that might lead to better or worse performance; and compilers are typically much better at low-level tasks like scheduling instructions and allocating registers.

Though compilers are now typically better than humans at register allocation, the C programming language retains the register keyword, which is supposed to be a hint to the compiler that it is appropriate to put a certain variable in a register. Compilers are not required to take this hint; the only mandatory effect of declaring a variable to be a register variable is that you are no longer allowed to take its address. And yet, one of the most popular attempted optimizations for the matrix multiplication assignment in my high-performance computing class is to add register annotations to a bunch of the variables, even though I tell the students that it will probably have no real impact. Why is this so?

I think the allure of register, along with the allure of more grotty C tricks like Duff’s device, is that it seems almost like a magical incantation. Wouldn’t it be nice if there was a formula that some wizardly programmer could chant over code in order to make it go fast? Of course, if there really is a formula, compiler writers now will incorporate it into the optimizer. This may not be good for the mystique of code tuning, but it certainly makes good engineering sense. After all, the magic of the computer is that it can automate things for which you can find a formula!

What is interesting about these tricks is not that they represent a sort of lore on how to optimize things now, but rather they are historical artifacts that show something about the joint evolution of machine architectures, compilers, and programming wisdom. They aren’t magical in any real sense, but they surely are worth enjoying as evidence that programmers in the past were just as clever as we fancy ourselves to be today.

Comments

blog comments powered by Disqus
Fork me on GitHub