- But I also knew, and forgot, Hoare’s dictum that premature optimization is the root of all evil in programming.
- — Donald Knuth
Something bizarre happened on the Groovy-dev mailing list the other day. Alex Tkachman made what I thought was a simple suggestion: since some rarely used features of the language make it slow, we should have a keyword or annotation which says to the compiler, “Hey there, compiler, I’m not using those features in this code, so please give me the faster code.” I had expected the others to debate the pros and cons of the idea, but instead, they couldn’t quite understand it.
They kept saying you’d need to check that the features weren’t actually used at runtime. I suggested the checks could work like assertions: have them on while testing, but off when performance mattered. But that didn’t seem to sink in. At one point, they even said that turning off these features would “betray” the core values of the language.
For most proposals they ask for or provide use cases and debate the pros and cons. But not this time. They seemed to think that any concession to performance is an ugly hack, that a sufficiently smart compiler or runtime system could get code just as fast. Even though it wouldn’t have nearly enough information. They even said that providing a way to turn off unused features would reduce their incentives to make Groovy faster. Much the way driving a car means kids these days never learn how to ride a horse. Or something.
These are very smart, very reasonable people. They’re very good finding the pros and cons of any proposal, and have wisely rejected some knucklehead suggestions of mine. And it’s worked spectacularly well, Groovy’s syntax and semantics are very close to my ideal language. So why don’t they want to make even the smallest, simplest concessions to performance?
Bjarne Stroustrup, inventor of C++, recently talked about OOP (Object Oriented Programming) before C++:
My firm impression (at the time) was that all sensible people “knew” that OOP didn’t work in the real world: It was too slow (by more than an order of magnitude), far too difficult for programmers to use, didn’t apply to real-world problems, and couldn’t interact with all the rest of the code needed in a system.
Twenty five years later, I think we’re in the same situation with dynamic languages. At the moment, languages are divided into two very different camps. In one corner, there are those that emphasize performance. They’re statically typed. They’re compiled, and you have to call the compiler explicitly. They try to make the efficient thing easy and the inefficient thing hard. And the language designers think very hard about the performance implications of every feature they add. C/C++, Java, C# and Ocaml are all in this group.
Then there are the dynamic languages, like perl, Python, Groovy and Ruby. They try to provide the maximum flexibility, because hey, they’re only for something quick and dirty, or something that will spawn other processes, right? Ok, ok, you might want to do some real work with them, but the libraries that do serious computation are all written in efficient language, so you’ll spend all your time in compiled code, right?
Well, it doesn’t always work like that. Matlab started as a wrapper for LINPACK, but now compiles to byte code on a virtual machine. And if you’ve ever tried to take a bunch of XML and turn it into SQL, you know Python can be slow, even with lxml to do the parsing. Each tag is a small amount of text, so the inherent amount of work per tag is very small. If you handle each tag explicitly, they Python overhead will dominate.
Which is a shame, because with a little thought, you can get all the functionality you want, and only pay the runtime cost when you’re actually using the feature. Why can’t I use the slow features outside my inner loop, but turn them off inside for a speedup? In other words, why can’t I take my dynamically typed program, profile it to find the bottlenecks, then sprinkle around some type declarations to get a speed up? Why can’t these passages be as fast as the statically typed languages?
There are a couple languages that do this. Common Lisp is one, the only one that approaches the speed of C. The company I work for uses it for a search engine for air fares, and its mighty fast, while also being a lot more productive than, say, C++. ActionScript, the language of Adobe Flash, does it, as do Visual Basic, VBA, and VB.NET. (Thanks to Alex Staubo and Steve Cooper for pointing those out.)
Failing that, how about a fast dynamic language? Features are often developed in slow languages first, then moved to fast ones, as the C++ example shows. And there are three projects I’ve found that could be the fast successors to today’s slow languages:
- Ng (source repository) is a dynamic language for the Java Virtual Machine by John Wilson. As he has said:
I’m implementing this thing back to front. Firstly I’m building a high performance runtime system for a fully dynamic language on the JVM. I’m then building an AST implementation which can be directly executed (like JRuby) or serialised to XML or to bytecodes. Finally I intend to design the language and compile it to my AST.
The goal is to generally be no more than half the speed of Java, but with full dynamic behaviour. Ng has some impressive benchmarks, but its in its very early stages. John Wilson helped create Groovy, so he has a track record of his ideas coming to fruition, but that’s no guarantee.
- Cython source files have Python syntax & semantics, but compile to C/C++ rather than Python byte code. Python can call them and vice versa, so you can use all your favourite Python libraries and other Python code. They’ve also extended Python syntax with static typing, and the ability to create pure C/C++ functions (which can’t be called from Python byte code). It’s designed to more or less reach the efficiency of C when things are typed. And it seems to be getting a lot of interest.
- PyPy plans to eventually incorporate a Just In Time compiler for Python, they way psycho did. But the project is very ambitious and moving very slowly. We’ll see what comes of it and when.
Premature optimization is the root of all evil. Why am I forced to optimize when I’m choosing my language, before writing a single line of code? I wonder whose going to be the next Bjarne Stroustrup, to become famous for inventing the first widely used fast dynamic language?
In the mean time, its a shame Groovy won’t let programmers specify what parts of their program aren’t using advanced features. Groovy is so close…
UPDATE: Mike Pall, the developer of the LuaJIT (among other things), had a great comment on the reddit page.