Execution speed comparison of “chord scale theory” operations among Python, Scheme, and OCaml implementations.
My reimplementation of MJB2Lite in OCaml continues. The OCaml language is a lot of fun to write in. Do yourself a favor and go program something useful in it :-)! Coding under a type inference system is really no more difficult than thinking about types in conventional strongly-typed languages, except you seldom have to specify the types explicitly! The OCaml compiler determines the types of your values and variables and ensures that their usages are correct. Obviously there is a big difference in execution speed when a compiled program is executed than when it’s interpreted. Perhaps also obvious but equally important: statically type-checked programs don't need to perform type checking at run time and run significantly faster, whether as byte code or native code.
I’d like to get a general idea of how much execution speed I gain by switching from Python to OCaml. I'm not trying to perform a general programming language benchmark, such as this one at Debian. That requires a lot of resources. But then idealized workloads can only give you so much idea on what will happen when you use these languages for your own programs anyway. So here I describe a comparison of Python, Scheme, and OCaml implementations of a certain “chord scale theory” operation in my automatic accompaniment program. The operation is one of determining for each chord that can be represented in my system those scales that are “compatible” with it. Compatibility here is taken to mean that notes in the chord are a subset of those in the scale. I described the use of this information in a harmonic analysis algorithm a while ago.
The Python version used was 2.3.5, the one that comes standard with OS X. The Scheme implementation used was Chicken version 2.6. The “Chicken compiled” test was generated by Chicken’s “benchmark mode” (the
-Ob option). OCaml 3.09.3 was used for the OCaml tests. All tests were run on a 1.83 GHz Intel Core Duo iMac. Here are the execution times of the tests:
From the chart, the reader should note that even OCaml byte code runs 5, 10, and 33 times faster than the compiled Scheme code, Python, and interpreted Scheme code, respectively. Compiling the OCaml code gets an additional factor of 4 speed up.
I should point out that many Scheme implementations exist and some will probably generate better code than Chicken. Also, the quality of the code generated by a given Scheme compiler seems to depend greatly on what code is being compiled.
I also measured the execution times of Python and OCaml implementations of the harmonic analysis algorithm itself. In one test, they were 0.620, 0.0342, and 0.0104 second for the Python code, OCaml byte code, and OCaml native code, respectively. Therefore the Python code is slower than OCaml byte code by about a factor of 18, which is in turn slower than OCaml native code by a factor of 3.
I’m very encouraged by the 40 and 54 times speed increases in two of my critical routines. Even if I use only OCaml byte code for some of the added flexibilities it allows (such as the use of the Dynlink library), I will still get 10 and 18 times speed increases, which, because we’re comparing interpreted byte code to interpreted byte code, is not to be sneezed at! And with OCaml’s equal if not superior expressiveness, I wonder if there’ll ever be an occassion on which I'll find Python to be a more suitable implementation language.