Why does calling a function without its owner is slower?
Asked Answered
H

1

7

If I do the following:

var abs = Math.abs;

Shoudn't abs(-10) be faster than Math.abs(-10)? Because abs is called directly.

This is what called my attention: Math.abs vs custom abs function Result of a test done at jsperf.com

Update:

The same test performed in the Internet Explorer 11 shows a completely different result: Result of a test done at jsperf.com

I'd speculate that this is due to some optimizations on built-in functions in Chrome's V8 Engine.

A test created by nnnnnn that clarifies what I am trying to say: Property shortcut Result of a test done at jsperf.com

Hew answered 30/11, 2013 at 14:39 Comment(28)
Have you tried the same test with a function that isn't built into the language?Selffertilization
Why would it be faster to use an additional variable to call the same function. I don't think you understand how caching and passing by reference works.Circumambulate
"Shoudn't abs(-10) be faster than Math.abs(-10)?" Err, why on earth would you think that? Because it's fewer characters?Taishataisho
I have a feeling that this is equal on most browsersVanderpool
@Circumambulate and @meagar - couldn't it be faster with a function that isn't built in to the language? With var shortcut = someObj.someOtherObj.someFunc you can then avoid having to do property lookups if you use shortcut()... Assuming the variable is local to where you're using it...Selffertilization
@Vanderpool - apparently it's not, but if you look at Ops/sec, that's a lot if iterations, so for just about any real life use, it is.Circumambulate
that benchmark is easy to cheat. capture the results for a more realistic comparison and (a guess here) closer results between the two.Shogun
@Circumambulate I found a test showing the opposite in general its faster in chrome locally and equal on average jsperf.com/abs-valueVanderpool
Just to be pedantic, JavaScript functions don't have "owners".Methaemoglobin
@Selffertilization - indeed, depending on the structure, caching the function would be faster than looking it up in an object.Circumambulate
@Circumambulate - Yes, here's a test where it's faster (in Chrome) for a simple object with one method: jsperf.com/property-shortcutSelffertilization
@Vanderpool - that perf seems to test the native Math.abs against other implementations, and not against passing it as a reference.Circumambulate
abs() needs a closure where Math.abs() doesn't. on browsers with decent closure penalty, it's more noticeable than the native Math object's lookup... Still, i can't imagine it would make much of a diff outside of benchmarks, nor is the discrepancy consistent, limiting it's use in premature optimization.Shogun
var abs0 = Math.abs; is the local one in that testVanderpool
@Vanderpool - that would be the native method, and the others are implentations in javascript with the same functionality, it has nothing to do with caching the function in a variable ?Circumambulate
@Circumambulate there are two tests on the native one cached and one uncached in that test I don't see what you meanVanderpool
@Vanderpool - sure, didn't notice that, and the difference is so small it's just about equal, but the "local" is somewhat misleading, as it's still the same function, just called with an extra variable holding a reference to the original function.Circumambulate
Thought that was the entire question, I just wanted to provide contrary evidence that its prob per browserVanderpool
I think the question was why it isn't faster to cache the function in a variable, but the function isn't really cached, it's passed by reference, and as it's a native function that is generally fast to look up, there is no benefit in using an extra variable to hold the reference.Circumambulate
There should be no difference, you did something wrong with jsperf... abs jsPerfAfter
@Circumambulate What I understand by doing Math.abs(-10) is that JavaScript has to access Math and then call abs. And by assigning a Math function to a variable would eliminate the hassle of accessing Math's attributes.Hew
Math is native standalone object, so accessing should be faster than a custom object or variable and maybe i'm wrong but if you look at steps, First look at Math object and then get abs function by reference and if you put reference to variable, will be the same +- 1 step ;)After
it would really be helpful to see the actual setup/test cases that generated those performance results.Afro
I'd speculate that Chrome's JS to native code compiler has some intrinsic code for Math primitives and will inline the calls. Putting the function into a variable defeats that optimization somehow. Note that in Firefox they have identical performance.Centering
Ignoring most of the comments, please update your post to indicate why you think one should be faster than the other. And remember that JavaScript is not executed the way you write it, all modern browsers do JIT compilation to "other code" which is then run instead. How they do this differs per browser, so for which JavaScript engine(s) are you asking this question?Literate
@Mike 'Pomax' Kamermans I organized the post and added the same test done in Internet Explorer 11.Hew
Check the "with" operator.Burlington
As i said earlier you did something wrong with your jsPerf (or something wrong with jsPerf), when you declaring your variables with var maybe it's creating closure or at every function call it's pass that variable as arguments to that function... or something else. look at jsPerf without var statementsAfter
B
3

This answer was rendered useless by Givi. See comments.

Looking up a user-defined function in a user-defined object is slower than looking up a function bound to a local variable, so you were mostly right.

However, looking up Math.* functions is faster, most probably because of internal optimizations of the V8 engine (so "caching" built-in functions in a local variable is actually slower, while "caching" user-defined functions is faster).

Edit: here's a jsperf demonstrating how Math.* functions are faster than their var x = Math.x counterparts, while doing the same for user defined function is not. It's just how V8 works imho. test results

Edit #2: just now i noticed this line from your question:

I'd speculate that this is due to some optimizations on built-in functions in Chrome's V8 Engine.

I'm not 100% sure, but it definitely looks that way, yes.

Ballard answered 2/12, 2013 at 8:44 Comment(5)
Open your javascript console and put this script... ### (function() { for (var i = 100000000, start = new Date().getTime(); i > 0; i--) { Math.abs(-111); } var end = new Date().getTime(); var result = end - start; console.log(result); }()); ### for checking Math.abs and this one for var abs = Math.abs; ### (function() { var abs = Math.abs; for (var i = 100000000, start = new Date().getTime(); i > 0; i--) { abs(-111); } var end = new Date().getTime(); var result = end - start; console.log(result); }());###After
Math.abs -> 23609 ; var abs = Math.abs -> 1125 . That nails it... so i guess jsperf is not to be trusted. Thanks Givi.Ballard
look at jsFiddle would be better if you copy source and paste directly to javascript console.After
I'm sorry, I made a mistake. Testing code directly in console is bad idea, so would be better if you create new html file and then pass source in empty body tag. source codeAfter
Running your jsFiddle, on my machine at least, they yield similar results, so Math.abs is 0% faster than var abs = Math.abs, tested with different numbers of loops. This can only mean that the environment the javascript runs in makes quite a difference in processing speed. That means any performance tests that we make should either account for the environment or are not to be taken as absolute/irrevocable results (also depends on the tests themselves of course). This was quite enlightening Givi, thank you :)Ballard

© 2022 - 2024 — McMap. All rights reserved.