9

It is a known fact that using arguments improperly in JavaScript may result in a function not being optimisable (see here and here by the end):

function notOptimisable (a, b) {
  // Optimising compiler says: Nope.
  var args = [].slice.call(arguments)
}

However, none of the sources have so far managed to explain why this prevents the optimisation from happening.

It is even more mind-boggling as all I have to do is

function optimisable (a, b) {
  // Optimising compiler says: I can do this!
  var args = new Array(arguments.length)
    , i = 0

  // Copy the arguments into an actual array, very carefully...
  for(i; i < args.length; ++i)
     args[i] = arguments[i]
}

and voilá - I have a copy of arguments that is an actual array and the function can be optimised:

node --trace_opt --trace_deopt script.js # Exerpt below

[marking 0x24ba2c0bf0f1 <JS Function notoptimisable (SharedFunctionInfo 0x26b62a724859)> for recompilation, reason: small function, ICs with typeinfo: 3/3 (100%), generic ICs: 0/3 (0%)]
[disabled optimization for 0x26b62a724859 <SharedFunctionInfo notoptimisable>, reason: Bad value context for arguments value]
[failed to optimize notoptimisable: Bad value context for arguments value]
[marking 0x24ba2d0041b1 <JS Function optimisable (SharedFunctionInfo 0x26b62a7247b1)> for recompilation, reason: small function, ICs with typeinfo: 7/7 (100%), generic ICs: 0/7 (0%)]
[optimizing 0x24ba2d0041b1 <JS Function optimisable (SharedFunctionInfo 0x26b62a7247b1)> - took 0.039, 0.164, 0.051 ms]

Thus, I ask thee:

Why?

  • What technical challenges prevent the optimisation from taking place?
  • Why can't the v8 engine simply return a standard array of arguments, just like the second code sample shows and optimise the function?
  • For extra points, why is the arguments only an "array-like" object from the language design perspective (i.e. it has numeric keys, but it is not a descendant of Array) and does that somehow play a role in the optimisation process?
Robert Rossmann
  • 11,931
  • 4
  • 42
  • 73
  • 1
    I think the problem is passing `arguments` to other functions, which requires all kinds of weird stuff (read the spec on arguments objects) that prevents optimisations. Because V8 cannot know that the called function (`slice`) doesn't use this weird stuff. – Bergi Mar 22 '15 at 18:59
  • How to do better: http://jsperf.com/array-with-and-without-length/3 – Bergi Apr 03 '15 at 20:36
  • @bergi how to do it twice as fast again: http://jsperf.com/array-with-and-without-length/5 (spoiler, loose the `switch`) – Orwellophile Oct 17 '15 at 08:10

3 Answers3

6

There are no insurmountable technical challenges. It was just a shortcut decision made during implementation of arguments object in Crankshaft: to support only cases where arguments object materialization can be easily avoided.

Even if Crankshaft supported materialization of arguments object the resulting code would still be slower than code that does not allocate arguments object.

It's just a question of supporting the fastest case in 10 minutes vs. supporting slower, but more generic case, in 10 days. (10 minutes / 10 days are imaginary numbers, I am just trying to convey the difference in scale of the implementation complexity).

If one wants to support the case when arguments object is materialize (and potentially leaks) then one needs to account for aliasing between arguments object and parameters - which changes how SSA form is constructed for these variables. This also complicates inlining for similar reasons.

A more generic approach to arguments object should have been based on the escape analysis / allocation sinking pass - but Crankshaft did not have anything like that when it was implemented and it still needed to support at least some fast path for arguments manipulation.

Vyacheslav Egorov
  • 10,302
  • 2
  • 43
  • 45
  • much praise for helping develop v8, taking the time to give such a long honest reply, for using a mac, and supplying this useful `--js-flags="--trace-deopt --code-comments"` argument to chrome. – Orwellophile Oct 17 '15 at 07:51
2

arguments is not an array, for example, consider:

function abc(a) {
    arguments[0] = 1;
    console.log(a);
}
abc(0);

The problem here is that the assignment of arguments[0] = 1 changed the value of a magically, I.E. arguments[0] aliases a. It's the same problem that eval and with causes - you can no longer statically see what variables are being changed and when.

Under strict mode which removes the aliasing misfeature there is no technical reason why this wouldn't be optimizable, however it might have been deemed to be not worth it as most of the web doesn't use strict mode in the first place or remove it for production code.

Esailija
  • 138,174
  • 23
  • 272
  • 326
  • What about strict mode then, would it be optimisable there? – Bergi Mar 22 '15 at 19:01
  • @Bergi practically there should be no problem it just simply isn't being done for strict mode either - most of the web isn't using strict mode anyway – Esailija Mar 22 '15 at 19:03
  • I already know it is not an array. The question is really about something different... – Robert Rossmann Mar 22 '15 at 19:05
  • I am looking. All the example reveals is that the named argument variable **is identical** to the item in `arguments`. How does that answer the question of why v8 is unable to optimise such function? – Robert Rossmann Mar 22 '15 at 19:08
  • 1
    @RobertRossmann really? you don't see anything weird in `a` changing its value even though the code never touches `a`? – Esailija Mar 22 '15 at 19:10
  • That's what I just said, albeit in a more complicated way.:) Still, the main question stands: How does that prevent the optimisation from happening? – Robert Rossmann Mar 22 '15 at 19:11
  • @RobertRossmann Well it's only the same problem as with `eval` and `with` - you can no longer statically see what variables are changed and when. – Esailija Mar 22 '15 at 19:14
  • @Esailija: Maybe not the web, but I guess most node libs do use it - and especially all the well-written libraries that could actually use this optimisation. – Bergi Mar 22 '15 at 19:16
1

The reason might be because JavaScript does not implement actual arrays in the traditional sense. Arrays are more like JavaScript objects, i.e. {}, that have a length property. The fact that arguments is not an Array was design mistake in the language. This is likely the reason why the optimizer has problems with this.

beautifulcoder
  • 10,832
  • 3
  • 19
  • 29
  • Okay, this helps understand why `arguments` is not an array, but how does that affect the optimisation? v8 does not seem to have problems optimising objects or arrays. – Robert Rossmann Mar 22 '15 at 18:58