In Thomas Fuchs’ latest JavaScript performance presentation he talks about the speed gains that can be experienced by using “unrolled” loops.
A conventional loop:
for (var i = 0; i < 10; ++i) { doFoo(i); } |
The “unrolled” version of that loop:
var i = 0; doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); |
A partially unrolled version:
for (var i = 0; i < 10; ) { doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); } |
Interestingly, speed gains can be experienced dependent on the loop size, albeit marginal at best. I thought about ways to build this into a clever forEach
function and came up with something that ‘pre-compiles’ functions containing partially unrolled loops. Have a look:
var forEach = (function() { var fns = [], callers = "true", numberFn = 10, i = 1; for ( ; i <= numberFn; ++i ) { callers += "&&f(a[++i])!==false"; fns[i] = new Function("a", "f", "l", "var i=0;while (i<l) {"+callers+"}"); } return function( array, fn ) { var len = array.length, n = numberFn, i; while (i = n--) { if ( len % i === 0 ) { return fns[i](array, fn, len); } } }; })(); |
This function will run one of 10 ‘pre-compiled’ functions on the passed array, dependent on the highest factor of the array’s length. I’m only creating 10 different functions in this example, you could create more.
If you were to pass an array with a length of 14, then fns[7]
would be used, since 7 is the highest available factor (the highest number below 10 that 14 can be divided by, to gain a whole number). fns[7]
looks something like this:
function anonymous(a, f, l) { var i = 0; while (i < l) { true && f(a[++i]) !== false && f(a[++i]) !== false && f(a[++i]) !== false && f(a[++i]) !== false && f(a[++i]) !== false && f(a[++i]) !== false && f(a[++i]) !== false; } } |
The !== false
part is used to create the effect of loop-breaking. Notice that the success of this boolean expression is depended upon to continue the chain of expressions (a && b && c
)
I’ve only tested it briefly, and to be honest, there doesn’t seem to be a notable benefit. In IE, I can see a bit of improvement over the conventional forEach
implementation but only if I’m using arrays with 1000+ lengths. I think this would only be useful in situations where you absolutely have to squeeze every inch of potential performance out of your app. Anyway, it’s still pretty interesting, I wonder what other fancy things can be created by using pre-compiled functions.
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Nice post, interesting read 🙂
Unrolling loops is a nice old technique but you’ll rarely need it these days (fun to read on how it’s been done, though, like Duffs device). Even JS works pretty fast on modern machines, nowadays. You should see a bigger difference on older machines though, something to keep in mind for testing. Apart from that, you’re obviously right that you need to be iterating over A LOT of elements for this to be worth it.
One point though: wouldn’t it make more sense to avoid the new Function in favour of a function literal?
@Fake51 — If you were to use a function literal then you’d have to type all the calls out, one by one. The benefit of using the Function constructor is that you can construct the body of a function as a string. It’s just a space/time saver.
@James: My main concern is just that using new Function is not as efficient as using a function literal (for one thing, it can’t be optimized by a compiler). Hence, you’ll save time while writing things at first only to lose it later. Seeing as you’re trying to save run-time, it came across as somewhat counterproductive.
I can’t help but discourage use of this technique. The overhead of calling the evaluator for
new Function
probably undoes the negligible speed gained by such unrolling. But the point is moot. First of all, unrolling the loops greatly obfuscates what the code is actually doing and it makes it harder to maintain in the future. Also, JavaScript is fast enough now days that we don’t need to worry about speed on this level. Just start caching expensive function calls where you can.There are only two places where I have seen a
new Function
and haven’t cringed:1. John Resig’s micro-templating
2. Oliver Steele’s Functional JS
I have larger problems than this in general though. There is a reason why we are programmers or developers or computer scientists or whatever you call it. It is not to do tedious repetitions of typing. It is to abstract what the computer does in to more general, modular patterns that are easier for us to use. Faster for us to develop code in. This is the whole point of high level languages like JS.
I’m not trying to personally attack you at all, I really hope that this hasn’t come across that way. I just think people should know that this isn’t good practice at all. If I ever end up maintaining code someone wrote with this technique I would pull my hair out and cry.
@Fake51, I see your point. Function literals would be ideal, but they’d take up quite a bit of space…
@Nick, I agree with you. I’m not intending on using this anywhere, I just thought it was a nice experiment. You know how it is — you watch a presentation, you see something, you want to code it up straight away… I just wanted to see if the speed increases would be significant.
Although, I have to disagree with your general opposition to the
Function
constructor. E.g. John Resig’s templating engine is something that just works, and fast too, does it really matter how it works as long as it works efficiently? Without using theFunction
constructor in a templating engine you’d have to come up with some meta-language and then parse it every time. Why not just use JavaScript? I must admit that Resig’s solution is a bit cryptic, and I think he’s sacrificing a lot of readability for a little bit of space-saving, but still, I think the use of theFunction
constructor is perfectly okay in situations like these. He’s provided an abstraction that can be relied upon by other developers, much like jQuery itself. There are lots of nasty little things going on in the jQuery core but it doesn’t matter, because it works. 🙂But yes, just to reiterate, the techniques discussed in this post are certainly not best practices, and I wouldn’t recommend their usage in 99% of situations.
you should read this article : http://www.peachpit.com/articles/article.aspx?p=31567
i’ve fiddles with loop unrolling just as you describe, with dynamic code being executed. the performance is very poor compared to inverse looping with do-while and propper accessing of DOM objects.
in Javascript, there are zillions of things to considder when doing performance optimizations – especially because of the fact that there are 3-4 browsers to test your script in… some tips don’t apply in IE, some not in FF and vice versa…
Chrome is majorly faster then all other browsers — unless you are cought in one of the zillion event bugs.
in Chrome 2.0 i experienced 100 mousemove events on every tick ! – these bugs are gone in 3.0 though.
you can read some about performance tips on my blog : blog.mdk-photo.com
the ones about String concat and innerHTML are the best i think
BTW…
even if you do a loop unroll in real code, its not faster !!!
i did so with a script…
i created 30 functions, each of them doin N-1 function calls
it actually ended up being slower then the normal 2-level For loop
@James
I think you may have misinterpreted what I meant when I was referring to John Resig’s micro-templating, I should have been more clear. I was not trying to trash his use of it, I was pointing it out as one of the exceptions to the rule. JS masters can indeed use it for great benefit, but I feel like I know JS inside and out, and I still wouldn’t use it in my own code because I wouldn’t trust myself. Oliver Steele’s prototyping of the String object and use of the Function constructor to create anonymous functions from strings is just brilliant as well.
They are both hacks though. Not in a bad way, but it really takes stepping outside of the language paradigms to create either of these two examples. I guess I’m just stuck inside the box.
Also, I understand what you mean when you say you were excited about something, but wouldn’t use it in production code. Just for the fun of it, I made a proof-of-concept of macros in JS (with regexps–ugh!–it was just a proof-of-concept!) but I was similarly scolded for even trying it. 🙂
If you’re interested here’s a link: http://fitzgeraldnick.com/weblog/3/
definitively one of those “things that make you go hmmm” moments 🙂 – not always practical (or best practice) but surely something to always have in your mental tool chest, just in case!
I guess I must be grateful that at least the text here wasnt white text on a black background, still I had to ad-block those pixellated looking graphics that made for a visually distracting reading experience!