1

Is there a performance benefit in switching from func.apply(obj, params) to func.call(obj) when params is an empty array or null?

I mean, is calling func.call(obj) any faster than calling func.apply(obj, null)?

I'm mostly interested in performance under NodeJS 4.x.

This is for an algorithm that has to make a lot of such calls.

vitaly-t
  • 24,279
  • 15
  • 116
  • 138
  • 2
    I realize that such an algorithm may make a lot of those calls, but its usually premature optimization to worry about such a trivial detail that bloats your code *before* profiling. – Jared Smith Oct 10 '15 at 14:16
  • This is not being helpful. I'm asking for some real numbers for this sort of optimization, not to tell me how to do my own measurements or approach writing my algorithm. – vitaly-t Oct 10 '15 at 14:21
  • The thing is, as with almost all micro-optimization, that the actual performance of a minuscule operation like this is dependent on the algorithm that uses it. You might well find that in isolated cases one is faster than the other, but in practice it's the other way around. There's no way to know for sure without trying it in the real use case. – JJJ Oct 10 '15 at 14:24
  • In my case I have to rely heavily on this kind of calls. Anyhow, the answer by Magu is excellent, and the numbers speak for themselves! – vitaly-t Oct 10 '15 at 14:27
  • Well the difference is 0.00000003 seconds per operation on my computer... but sure, if you have a million calls in a loop you'll save 30 milliseconds. – JJJ Oct 10 '15 at 15:37
  • According to the tests (see the link below), `apply` is 3.5 times slower than `call`. From optimization point of view, the difference is huge. That's the whole point of the question. – vitaly-t Oct 10 '15 at 15:44
  • It is huge *on that single operation*, but it won't make the *entire algorithm* run 3.5 times faster. If it's the bottleneck of the algorithm then sure, it helps a lot. But if the entire algorithm takes, for example, 200 milliseconds to run, and the function call's part of that is 1 millisecond, the "optimization" will drop it from 200 ms to 199.7 ms. That's why it's important to optimize in context. – JJJ Oct 10 '15 at 17:08
  • @Juhana, that wasn't in the question. – vitaly-t Oct 10 '15 at 17:10
  • No, but it's what matters in real life. – JJJ Oct 10 '15 at 17:11

3 Answers3

3

On this page there is a comparison. https://jsperf.com/call-apply-segu Call was faster on my machine.

Markus
  • 3,871
  • 3
  • 23
  • 26
2

Basically, they will do the same steps:

Function.prototype.apply (thisArg, argArray)

  1. If IsCallable(func) is false, then throw a TypeError exception.
  2. If argArray is null or undefined, then
    1. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and an empty list of arguments.

Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

  1. If IsCallable(func) is false, then throw a TypeError exception.
  2. Let argList be an empty List.
  3. If this method was called with more than one argument then in left to right order starting with arg1 append each argument as the last element of argList
  4. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.

So the difference, if any, should be implementation dependent, and negligible.

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Thank you for the detailed explanation, but the answer by Magu gives the exact numbers, which is perfect for what I was looking for. – vitaly-t Oct 10 '15 at 14:38
1

Ha, interesting: it looks like apply is slower than call. 8-)

    ~/tmp ω  cat test.js                                                                                                   
function work(a, b, c) {
  // do some work
}

var a = [1, 2, 3];

for (var j = 0; j < 4; j++) {
  console.time('apply-ing');
  for (var i = 0; i < 1000000; i++) {
    work.apply(this, a);
  }
  console.timeEnd('apply-ing');

  console.time('call-ing');
  for (var i = 0; i < 1000000; i++) {
    work.call(this, 1, 2, 3);
  }
  console.timeEnd('call-ing');
}
    ~/tmp ω  node test.js
apply-ing: 42ms
call-ing: 5ms
apply-ing: 40ms
call-ing: 5ms
apply-ing: 42ms
call-ing: 5ms
apply-ing: 39ms
call-ing: 6ms
    ~/tmp ω  node --version
v4.1.2
    ~/tmp ω
Vlad GURDIGA
  • 1,284
  • 2
  • 14
  • 18
  • 1
    According to the link published by Magu, `apply` is a lot slower than `call`. – vitaly-t Oct 10 '15 at 14:40
  • it is an incorrect implementation. To justify everything better to use `call` like this `work.call(this, ...a)`. And apply will win. Just only because apply here uses argument config as an argument. It's more useful for realworld – JerryCauser Dec 26 '19 at 18:13