I recently caught up on some technical reading. I’m particularly enjoying posts related to functional programming. One downside to functional programming is the performance hit we take jumping scope and making extra function calls for things we could just do in a single block.
One of the more interesting ideas in functional programming is composition. The idea that you can combine two steps, like y = g(x); and z = f(y); into a single function z = f(g(x));. The typical compose function example I see takes two arguments, like the one in chapter 6 of Eloquent JavaScript. If you want to compose a third function, you have to call compose again, z = compose(e, compose(f, g));.
This type of nesting is precisely the scope chaining we don’t need (provided we know all of the functions to compose). For instance, a compose function designed to take three arguments, z = compose(e, f, g), will outperform the two calls to the compose function with arity of two. So, based on my work with get, I wrote a memoizing compose that takes any number of functions.
some simple examples of usage:
EDIT: Based on feedback from a colleague, I removed memoize and used spaces instead of ‘\t’ in c to make the implementation more clear and less distracted. The old version is still in my gists, #4747439.
My main project at work uses large JavaScript objects for a data store. Our team used a very cool method to dynamically create null-safe getter functions and cache them for reuse. Oh, and did I mention it was fast?
I rewrote the guts to do just the bare minimum*–get objects and their primitive property values from a given context (or this). Future implementations may take some sort of descriptive notation in the path (mostly likely to filter arrays of objects), but this will work well for most applications.
* note: So, slightly more than the bare minimum. I chose to allow path to be a string or array of strings. This allows me to pass get.apply an arguments object–which may come in handy–and saves processing if I already have a split path.
You may have noticed that in my last article, I mentioned I would rather loop through copying arguments than slice them. That came out of this research: jsPerf.
var args =[1,2,3,4], i,
len = args.length,
arr =newArray(len);for(i =0; i < len; i +=1){
arr[i]= args[i];}
With relatively short lists, copying out performs slice by 4x or more (your browser mileage may vary). This is important for low-level frameworky things that get invoked a lot. Since well-designed methods often have signatures under 10 (hopefully way under 10), looping is faster in every browser I tested. Safari had the most interesting results in that copying was always faster. It makes me wonder how that native implementation really works.
Part of the technique is pre-allocating the array length you will copy into. That seems to make a difference in some browsers so it’s worth mentioning that I used that technique in my tests. I believe this makes a difference because the looping copy process does not need to change the allocated memory for the target array as we add new values. If that were the case, I would expect lower thresholds in the browsers where this matters, like Chrome, see jsPerf.
Lastly, I found an interesting jsPerf test around the same idea except I noticed they constructed the test incorrectly, testing the performance of function construction instead of just the cloning action. I rewrote the tests with what I learned from the other test into this one: jsPerf.
function clone(arr){var i, len = arr.length,
arr_clone =newArray(len);for(i =0; i < len; i +=1){
arr_clone[i]= arr[i];}return arr_clone;}
In conclusion, even when converted to a method, this technique can outperform native methods on short lists (although much less so). Given the smaller differences as a method, I would not advance the idea into a clone method on the Array prototype that switches techniques based on the current length–that additional logic would probably bring the overall speed down to native levels. Either copy in-line (only with small lists) or use a native method when performance matters.
By using a simple approach to curry and enforcing a strict signature and return value, we can make performance improvements to the traditional curry implementation.
Case 1: Currying can be useful in JavaScript for making code DRYer and more reusable because you can create several derivative functions from a single function. This differs from Partial Application in that Curry always partially applies a single argument while Partial Application may fix, or bind, any number of arguments.
This subtle difference can lead us to some performance gains. For instance, look at Douglas Crockford’s classic extension to the Function prototype from his book, JavaScript: The Good Parts:
Function.prototype.curry=function(){var slice =Array.prototype.slice,
args = slice.apply(arguments),
that =this;returnfunction(){return that.apply(null, args.concat(slice.apply(arguments)));};};
Performance Improvement 1:
By using the full arguments list, Crockford allows for partial application of arguments in the returned function. While convenient, most uses of the curry method will probably only pass a single argument. If we swap “slice.apply...” for “[arguments[0]]“, curry only uses the first argument passed and we have no need of transforming arguments into an Array using the borrowed slice method.
We still can not use concat directly (the reason Crockford sliced his arguments in the first place) because lacks generic qualities, see Dr. Rauschmayer’s article, Array.prototype.concat is not generic. Although Rauschmayer fails to point out that by applying concat, you can achieve the desired effect–essentially unwrapping the arguments array into separate arguments to be concatenated.
//Sadly, this will now perform slower than Crockford's implementation because concat will receive a longer arguments list.//an alternate using unshift instead will be about the same speedFunction.prototype.curry=function(){var f =this,
args =[arguments[0]];returnfunction(){return f.apply(null,Array.prototype.concat.apply(args, arguments));};};
This leaves us with a for loop as an alternative to populating the args Array.
Also consider, Crockford prevents the original function from having a context with a null parameter passed first to apply(). If we only plan to curry a single parameter, we can pass a context for use during invocation.
Function.prototype.curry=function(arg, context){var f =this,
args =[arg],
count =1;returnfunction(){var i, len;for(i =0, len = arguments.length; i < len; i +=1, count +=1){
args[count]= arguments[i];}return f.apply(context, args);};};
function curry(/* n,*/ fn /*, args...*/){var n,
aps =Array.prototype.slice,
orig_args = aps.call( arguments,1);if(typeof fn ==='number'){
n = fn;
fn = orig_args.shift();}else{
n = fn.length;}returnfunction(){var args = orig_args.concat( aps.call( arguments ));return args.length< n
? curry.apply(this,[ n, fn ].concat( args )): fn.apply(this, args );};}var testFn =function(a, b, c, d){return a + b + c + d;};
curry(testFn)(1)(2)(3)(4);//10
curry(testFn)(1,2,3)(4);//10
Developers who shun augmenting the native prototypes will find Alman’s function especially attractive. Another convenience of this implementation, apart from allowing partial application, invokes the original function upon satisfying all arguments. For instance, compare the invocation syntax:
While less verbose, one may find the dynamic nature of the return value troublesome to deal with. Consider a function you would like to curry which returns a function. How do you type check the results of calling curry? Additionally, while the optional “n” parameter may solve issues with functions which take n arguments, this complicates the maintenance of such code when curried functions change their signatures. By all definitions I can find, currying a function should return a function (as should partial application). Alman’s example mimics the syntax of languages like Haskell or ML where functions return a curried version if passed less than the required number of arguments. Maybe a proper name for this function is invokeOrCurry.
Performance Improvement 2:
Simply removing additional logic to always return a function result enhances curry performance. In fact, I often make “do less” the first performance enhancement of any exercise, but Crockford published first.
[Optional] Performance Improvement 3:
In cases where you will only pass simple, string/number-style arguments and you will keep the curried functions around for a while, you may want to use a memoizer to cache the functions generated. Due to the nature of test harnesses, I’ll keep this version generic.
Style Improvement 1:
With clear parameters and consistent return type, we can properly comment our new curry method and invite developers to use it:
When I look at code like the memoizer, I find things that others gloss over because of my training. My most recent project involves a huge application with brutal SLA’s. We must look at every function call or loop critically in order to achieve the required speed. That often means A/B testing in multiple browsers to see where we can shave a few milliseconds.
Here’s another example of what I’m talking about. How often do you see developers manipulate arguments in a function the way the memoizer did?
var args =Array.prototype.slice.call(arguments);
It’s pretty standard. Well, it may seem trivial, but nothing’s free. If you don’t have to do it, don’t–that’s the easiest way to boost performance. I wrote two example tests for the most common uses of arguments: iterating and prepending.
(function(){var i, len;//just iterate, you don't need an Array method (forEach is slow!)for(i =0, len = arguments.length; i < len; i +=1){
noop(arguments[i], i);}}(3,2,1));var list =[3];(function(){var i, len, llen;//we know getting length once is fast, so do it here then add themfor(i =0, llen = list.length, len = llen + arguments.length; i < len; i +=1){//determine which set to use based on i and the length of the prepended set//just use property access to get arguments out, no slice, no concat
console.log(llen > i ? list[i]: arguments[i - llen], i);}}(2,1));
I sent my first pull request on Github, this week.
“Welcome to two years ago,” you say.
Well, to be honest, I didn’t really understand the concept behind Github. A lot has changed in two years–I love my job.
I read Addy Osmani‘s article on the memoizer and I clicked a link. “Cool!” He wrote it up in Github. Wait, this code is different than the article. Someone made improvements?!
That got me thinking. I noticed a couple of things I could make better, so I forked the master and committed my changes. Knowing that IE7 has no native JSON support, I made the function fallback to just a no-op function. Second, I realized that there was no need to slice the arguments, you can stringify arguments and still get a unique cache key–a major performance boost. Then I made a pull request to suggest that the changes get added back to the master.
To my surprise, and delight, Jamie Mason merged my ideas. All-in-all, it was a rewarding experience and I can see myself doing it again. Social coding… who knew? I also see the value of writing code in Github then blogging it so the code conversation can continue after the article–much easier to manage than comments in a blog.
Based on the tests at http://www.broofa.com/2009/02/javascript-inheritance-performance/, I decided to add proto-q’s pattern to the race. At the core, it’s a modified AdHoc pattern, since it mostly relies on prototypes to handle method inheritance during instantiation.
Interestingly, object construction in general was above average speed on base class instantiation, below average on subclass instantiation, but a leader in all method invocation. Strangely, invocation of subclass methods was faster than all others, including AdHoc.
While trying to implement my CodeDodger loader script, (a fresh look at dynamic DOM injection for script and link tags using DocumentFragment) I noticed the JavaScript and CSS was not processed in IE. I did a lot of research and testing just to find out that IE does not treat an appended DocumentFragment the same as an appended element.
When comparing my script to that of a simpler script to load css/js dynamically on Dynamic Drive, I found virtually no difference in methodology except DocumentFragment. I still tested every possible way to construct the link/style tag but all had the same results. Strangely, the IE developer tool reports the DOM was modified successfully:
Even though the new DOM nodes appear correctly, the browser never processed associated files. Even stranger, the IE dev tool finds the JavaScript files and I can run the code in the console referencing the included files:
This caused me to wonder about the timing of my scripts in IE (of course, all other browsers work just fine). I tried wrapping my evocations (built dynamically in the page with PHP) inside a jQuery document.ready call and proceeded that function with:
IE always alerted “break” despite the files loading from cache and having several milliseconds to process before the page completely rendered. So, I tried another trick and wrapped my invocation inside
setTimeout(function(){//put evocation here,0);
That actually worked. My page included and processed the new JS files then executed the evocation on a “ready” DOM (it has something to do with the difference between document.ready and pageLoad and IE being IE).
I found no such work-around for the CSS files. So, if I want to keep CodeDodger geared toward high-performance, I will need to detect for IE and skip the fragment to insert the CSS link tag directly into the DOM. I know this will perform slower and could cause blocking but I don’t think I have a choice–thanks IE.
I checked out this great slide presentation on slide-share. I might have to hunt this guy down and find his presentation notes. The section on RDBMS (starting in slide 81) speaks the truth on matters of indexes, joins, and explain plans. George mentions prepared statements, the use of bind variables, but does not mention stored procedures.
I do not mean Java stored procedures, which I suppose only exist in Oracle, but actual database constructs. Emerging languages may not have the API support to boast prepared statements but you can still make database stored procedures that have all of the same benefits.
Also, a contentious bit of information, relational integrity constraints, like indexes, have performance penalties. If the application can ensure relational integrity, and the table needs to perform at high levels, you may want to forgo constraints. That causes some problems with IDEs for languages like Java which need that information to construct objects (oh, and it makes some developers spontaneously explode).
Edging out Chrome in anything remains a mark of achievement but will users feel like FF 3.5 actually browses faster than Chrome 2?
I reviewed the charts at the UA Profiler. FF only supports one feature that Chrome does not, link prefetching. Since only FF supports it and it requires new development practices, I doubt it will make much impact with users on most sites.
Only one quantifiable performance measure on the UA Profiler differs between Chrome and FF, maximum connections, with the edge going to Chrome. Couple that with the outstanding JavaScript performance in the new Chrome Chrome remains the browser to beat.