

Arguments Object: Doctor Jekyll and Mister Hyde in Node.js - chapel
https://plus.google.com/111090511249453178320/posts/ikjTyY6UKcE

======
willwagner
Just to highlight what for me was non-obvious but interesting. It implies that
this change in two places made a significant improvement to their performance
stats. Basically, the optimizing compiler is finicky about the arguments
object especially if it is used out of bounds. Without that knowledge, the
change seems non-intuitive. I wonder if this true for all pseudo-arrays?

Old Code:

    
    
      var args = Array.prototype.slice.call(arguments, 1);
    

New Code:

    
    
      var l = arguments.length;      
      var args = new Array(l - 1);
      for (var i = 1; i < l; i++) args[i - 1] = arguments[i];

~~~
mraleph

      > especially if it is used out of bounds.
    

Fortunately in EventEmitter.prototype.emit it was never used out-of-bounds.

    
    
      > I wonder if this true for all pseudo-arrays?
    

Arguments object has a very special semantics. It is specified as an object
with getter&setter for each indexed property. If somebody would write some
approximation of it in JS it would look like:

    
    
        function foo (x, y) {
          var arguments_parody = { 
            get "1" () { return x; }, set "1" (x_) { x = x_; }
            get "2" () { return y; }, set "2" (y_) { y = y_; }
          };
        }
    

It's a very heavy "pseudo-array" in this sense. And the code above is indeed
just a parody, real arguments object is more tightly integrated with the
language, e.g. it knows exactly how many arguments were passed smth that you
can't emulate in JS (without using arguments object). For more intricacies in
<http://es5.github.com/#x10.6>

What optimizing compiler tries to do is to avoid creating arguments object and
rewrite simple uses (simple is defined in the referenced post) to operate
directly on the stack instead of creating and passing around real object. I
should probably do yet another post about that but now with some assembly (but
my battery is running low atm) :-)

Doing something like:

    
    
        Array.prototype.slice.call(arguments, 1);
    

this use is not simple so compiler just gives up. But theoretically compiler
could have inlined slice and looked one level deeper to see that.

~~~
IsaacSchlueter

        > theoretically compiler could have inlined slice
        > and looked one level deeper to see that.
    

I'd suspect that optimizing Array.prototype.slice.call(arguments, n, m) at the
v8 level would yield pretty significant gains, even if no other arguments-
object abuse was supported intelligently.

For almost any complicated operation on the arguments, your first step is to
convert it (or part of it) to a true Array. Most libraries do that using
Array.prototype.slice, in my experience.

------
blinkingled
Funny how the commit for this change had no accompanying comment - this
particular change begs for a clear comment!

[https://github.com/joyent/node/commit/91f1b250ecb4fb8151cd17...](https://github.com/joyent/node/commit/91f1b250ecb4fb8151cd17423dd4460652d0ce97)

~~~
jrockway
A link to this Plus post would at least be nice.

~~~
blinkingled
Wave comes to mind, I prefer an inline comment! Just kidding, plus probably
isn't going anywhere :)

------
yonran
Does this mean that passing the arguments object to any function (even
Array.prototype.slice) will ruin your performance? This is extremely common in
many libraries' bind/partial functions e.g. Underscore, Closure, var_args in
Coffeescript.

~~~
mraleph
Arguments misuse will ruin performance if the code is hot.

~~~
yonran
I would hesitate to call the use of an object as defined in the ECMAscript
spec "misuse." Perhaps it is becoming a best practice to avoid it, but it's
not misuse yet until we know that it causes an unavoidable performance
degradation.

The lesson here is that one feature of V8 is an arguments optimization, but
you must never pass the object to other functions (i.e., you have to manually
inline the calls you would have made).

------
bauchidgw
hmmm... anybody knows if this is an issue for coffeescript generated
javascript, too?

~~~
TrevorBurnham
Sure. This would be a reason not to use splatted arguments in performance-
intensive functions, since

    
    
        foo = (first, middle..., last) ->
    

relies on `Array.prototype.slice(arguments)` to transform arguments into an
array. You could accomplish the same thing using a loop over arguments
instead, or better yet, design your code so that the function can take a fixed
number of arguments instead (just use an array as the second argument).

But remember, this is only going to matter for functions that are called
thousands of times in a short time period. Elegant code is usually better than
efficient code. "Premature optimization is the root of all evil."

