Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS: How long does it take to call a function?

So, I am programming a 2d Javascript physics simulation. The performance is good, but I'm going through making optimizations to make it better. So, because the program works with a lot of physical geometry, I make several Pythagorean Theorem calculations in the program. In all, about five calculations; together, they run about one million times per second. So, I figured it would boost performance if I put that simple Pythagorean theorem code into a new function and called it; after all, that way the browser has less compiling to do. So, I ran the code in Firefox and got.... a 4000000% increase in the execution time of that calculation.

How? It's the same code: Math.sqrt(x*x+y*y), so how does adding it as a function slow it down? I assume the reason is that a function takes time just to be called, without executing the code, and that adding that a million of these delays per second slows it down?

That seems rather alarming to me. Would this also apply to predefined js functions? It seems unlikely, and if so, how do they avoid it?

The code used to go like this:

function x()
{
    dx=nx-mx;
    dy=ny-my;
    d=Math.sqrt(dx*dx+dy*dy);
    doStuff(...
}

What I tried was this:

function x()
{
    dx=nx-mx;
    dy=ny-my;
    d=hypo(dx,dy);
    doStuff(...
}
function hypo(x,y)
{
    return Math.sqrt(x*x+y*y);
}

Thanks!

like image 865
mindoftea Avatar asked Apr 12 '12 23:04

mindoftea


People also ask

Are function calls expensive in JavaScript?

Function calls in Javascript are less expensive than the maintenance nightmare caused by poorly-organized code.

Does function call increase execution time?

Solution 1. In general yes, every time you call another method the CPU registers need to be stored and restored and parameters have to be pushed on the stack.

How do you call a function after some time?

You can use JavaScript Timing Events to call function after certain interval of time: This shows the alert box every 3 seconds: setInterval(function(){alert("Hello")},3000); You can use two method of time event in javascript.


2 Answers

Function calls are negligible or even optimizing in pre-compiled languages which JS has never been. Beyond that, a great deal depends on the browser.

They're the death of all performance in interpreted languages which JS has been primarily until fairly recently. Most modern browsers have JIT (Just In Time) compilers which is a huge upgrade from the JS interpreters of the past but I believe function calls to another scope still cost some overhead because JS's call object has to determine what is actually being called and that means marching up and down various scope chains.

So as a general rule: if you care about IE8 and lower and older versions of Chrome and Firefox avoid function calls period. Especially inside loops. For the JIT browsers, I would expect that a function defined inside the other function would be generally beneficial (but I would still test as this is brand new technology for IE9 and relatively new for everybody else).

One other thing to be wary of. If a function is particularly complex, JIT's may not do anything to optimize them.

https://groups.google.com/forum/#!msg/closure-compiler-discuss/4B4IcUJ4SUA/OqYWpSklTE4J

But the important thing to understand is that when something is locked and only called inside a context, like a function within a function, it should be easy for a JIT to optimize. Defined outside of a function, it has to determine which definition of that function is being called exactly. It could be in an outer function. It could be global. It could be a property of the window object's constructor's prototype, etc... In a language where functions are first class, meaning their references can be passed around as args the same way you pass data around, you can't really avoid that step outside of your current context.

So try defining hypo inside X to see what happens.

Another couple of general tips from the interpreted age that might still be valuable in JITs:

  • The '.' operator as in someObject.property, is a process worth caching. It costs overhead as there is an associated call object lookup process every time you use it. I imagine Chrome would not preserve the results of this process since alterations to parent objects or prototypes could alter what it actually references outside of a given context. In your example if x is being used by a loop (likely okay or even helpful if x is defined in the same function as the loop in JITs - murder in an interpreter), I would try assigning Math.sqrt to a var before using it in hypo. Having too many references to stuff outside of the context of your current function might cause some JITs to decide it's not worth the trouble to optimize but that's pure speculation on my part.

  • The following is probably the fastest way to loop an array:

//assume a giant array called someArray
var i = someArray.length; //note the property lookup process being cached here
//'someArray.reverse()' if original order isimportant
while(i--){
  //now do stuff with someArray[i];
}

note: code block not working here for some reason.

Doing it this way can be helpful because it basically morphs the inc/decrement step and the logical comparison into just the decrement, completely removing the need for a left/right comparison operator. Note that in JS the right side decrement operator means that i gets passed to be evaluated and then decremented before it's used inside the block. while(0) evaluates to false.

like image 137
Erik Reppen Avatar answered Oct 30 '22 05:10

Erik Reppen


To my surprise, caching the lookup as suggested by Erik doesn't do much to improve performance on my browser (Chromium, Linux) but seems hurts performance instead: http://jsperf.com/inline-metric-distance

var optimizedDistance = (function () { 
    var sqrt = Math.sqrt;
    return function (x, y) { return sqrt(x * x + y * y); }
})();

is slower than

var unoptimizedDistance = function(x, y) {
    return Math.sqrt(x * x + y * y);
}

Even calling an alias is slower

var _sqrt = Math.sqrt; // _sqrt is slower than Math.sqrt!

But then again, this is not an exact science and real life measurements can still vary.

Nonetheless, I'd go with using Math.sqrt.

like image 41
Otto Allmendinger Avatar answered Oct 30 '22 04:10

Otto Allmendinger