I decided to try writing the same thing in JavaScript and discovered something really strange.<p>My first idea was:<p><pre><code> numbers.map(function(x){return String.fromCharCode(x);}).join("");
</code></pre>
This was pretty fast already, but why not eliminate the anonymous function completely and pass String.fromCharCode directly to map():<p><pre><code> numbers.map(String.fromCharCode).join("");
</code></pre>
I timed it and... ...this was ~100 times slower than the previous version. WTF!<p>Somehow passing this native function directly to Array.map() is way slower than wrapping it inside another function and passing that to Array.map().<p>I have tested it so far in Chrome, Firefox and Opera - the results are the same. I also tried forEach(), which behaves similarly.<p>Does anybody have an idea why this is so?<p>Update: I tried the same with Math.round and Math.sin, and with these the results were as one would expect: passing the function to Array.map() directly was a little bit faster than using intermediate anonymous function. So it seems the problem is with String.fromCharCode specifically.
I once found myself writing a python program to calculate Poker hands. It was pretty fun to write - it generated, for any given hand you hold, every possible way the game can go forward, and generated statistics about how many times you win (against 1 opponent only, who could be holding anything.)<p>The program initially ran for 20 minutes on each hand. I worked a day on optimizing it, and got it down to around 1-2 minutes per hand, so I learned a few tricks to make things quicker. Lessons learned:<p>1. Local lookups are <i>much</i> faster.<p>2. Anything that you can somehow offload to a c-loop, makes things much faster.<p>3. Surprisingly, the fastest construct for building a new list was always list comprehension (e.g. [x for x in calc(l) or whatever.) This was far faster than a regular ol' for-loop, of course, but was also faster than using map's and reduce's.
It's a whole different story if the input gets longer and the quadratic complexity kicks in. Just change the testdata like this:<p><pre><code> testdata = range(256)*100
</code></pre>
Result:<p><pre><code> f1 5.539
f2 40.747
f3 5.072
f4 5.311
f5 0.068
f6 3.304
f7 1.431</code></pre>
The first thing that stuck out to me like a sore thumb was the string-concatenation-in-a-loop. Though the author was aware of its consequences he only addressed it late in the article, optimizing relatively trivial stuff like variable lookups first. And then he did so in a rather strange way, instead of adding string fragments to a list and then join()ing them.<p>Not sure how fast that loop would be in Python compared to the implied loop&join but I sure would have liked to see them compared.
For those interested, the author of this was actually Guido himself.<p>I would have used "".join(map(chr, list)) myself. But I am not sure that was support in the version of python available at the time. It is equivalent to f6.<p>To some of those talking about the dynamic nature of python means you have to look up chr every time, there is a way to stop that, see this project <a href="http://github.com/rfk/promise/" rel="nofollow">http://github.com/rfk/promise/</a>
Incidentally, are there any programmes that can scan your python functions and suggest improvements that are in this article? e.g. any programme that'll suggest referencing global names to the local namespace, using map etc.?
I hope that one day soon our compilers will be smart enough to optimize simple things like this automatically, so we can focus more on writing for clarity. This seems like exactly the kind of thing a next-gen compiler should be able to optimize.