From: Lasse Reichstein Nielsen on
Thomas 'PointedEars' Lahn <PointedEars(a)web.de> writes:

To nitpick out of context:

> 2. Several function calls will be always more expensive than just one and
> the creation of an Array instance.

That's not a given.

It's possible that four function calls take longer than creating an Array
and calling one function that loops over the array and extracts the elements
again (possible, but not given), but the latter also allocates space for the
array. If this happens often enough, the memory overhead will also cause
earlier garbage collection.

After writing that, I decided a test was in order, instead of relying on
intuition. The following code is attempted written without obvious
inefficiencies in either test case (all variables are local, loop
invariant expressions are lifted, etc.).
I have run it in Opera 10.51, Chrome 5(dev), Firefox 3.6.2 and IE 9 preview.

<script type="text/javascript">
function Obj() {
this.props = {};
var self = this;
this.setChained = function setProp(name, value) {
self.props[name] = value;
return setProp;
};
};
Obj.prototype.setProp = function (name,value) {
this.props[name] = value;
};
Obj.prototype.setProps = function (arr) {
var props = this.props;
for (var i = 0, n = arr.length; i < n; i += 2) {
props[arr[i]] = props[arr[i+1]];
}
};

function test1() {
var o = new Obj();
var t0 = new Date();
for (var i = 0; i < 100000; i++) {
o.setProp("a", i);
o.setProp("b", i);
o.setProp("c", i);
o.setProp("d", i);
}
var t1 = new Date();
return t1-t0;
}

function test2() {
var o = new Obj();
var t0 = new Date();
for (var i = 0; i < 100000; i++) {
o.setChained("a", i)("b", i)("c", i)("d", i);
}
var t1 = new Date();
return t1-t0;
}

function test3() {
var o = new Obj();
var t0 = new Date();
for (var i = 0; i < 100000; i++) {
o.setProps(["a", i, "b", i, "c", i, "d", i]);
}
var t1 = new Date();
return t1-t0;
}

alert([test1(), test2(), test3()]);
</script>


In the first three browsers (not IE), doing four calls is consistently
faster than creating one eight-element array and doing one call. And
not by a small amount - it is between 1.75 and 3 times as fast.
In IE9, they take about the same time.

Using the chained way of calling the setter is about the same as just
doing the four calls in Firefox and Chrome, and somewhat slower in
Opera, but not as slow as using an Array. In IE9, this approach is
actually the fastest. Go figure.

In summary: Function calls aren't significantly slower than other
language features like object creation and property lookup.
Object creation, and inspection, spends a lot of time in memory
access, where function calls probably uses the stack (which is
very fast memory, because it's almost always in the first level
cache of the processor).

/L
--
Lasse Reichstein Holst Nielsen
'Javascript frameworks is a disruptive technology'

From: Lasse Reichstein Nielsen on
nick <nick___(a)fastmail.fm> writes:

> On Apr 1, 6:30�am, Thomas 'PointedEars' Lahn <PointedE...(a)web.de>
> wrote:
>>
>> 1. It requires you to keep an external reference to the initial calling
>> � �object as Richard has already explained.
>>
>
> What about arguments.callee? That's good enough for returning a
> reference to the same function.

If performance is important, don't use "arguments" at all.

"arguments.callee" always referes to the function being executed.
You can just give that function a name and refer to it by that name.

I.e.,
DON'T: function (args) { ... arguments.callee ... }
DO : function foo(args) { ... foo ... }

/L
--
Lasse Reichstein Holst Nielsen
'Javascript frameworks is a disruptive technology'

From: Scott Sauyet on
nick wrote:
> On Apr 1, 4:38 pm, Scott Sauyet <scott.sau...(a)gmail.com> wrote:
>
>> nick wrote:
>>>   myApp.keys.register
>>>     ('w', panNorth)
>>>     ('s', panSouth)
>>>     ('a', panWest)
>>>     ('d', panEast);
>
>> I agree with the other posters that it is in fact less clear.  But
>> there are other options as well:
>
> Is it really so much less clear that you'd really spend any
> significant time wondering what that code did?

Perhaps not. I would definitely wonder *how* it's doing what it does,
though, and that's a distraction too. Chaining like this wouldn't
surprise me at all:

myApp.keys
.register('w', panNorth)
.register('s', panSouth)
.register('a', panWest)
.register('d', panEast);

There I would know exactly what's going on, and I'd understand how it
works too.


> I mean, I see what you
> guys are saying but I just don't see what else that code could
> possibly be doing. The name of the function appears once, and all the
> arg lists look the same, so  I feel like it's pretty safe to assume
> (to someone reading chained code like this) that the same function is
> getting all the arg lists.

I guess if the args all look so similar to one another as these ones
do, I'd probably make the same assumption, but the minute they start
looking different, I would likely get confused.


>>   myApp.keys.register({
>>     'w': panNorth,
>>     's': panSouth,
>>     'a': panWest,
>>     'd': panEast
>>   });
>
> That example isn't too bad. The issue is that 'register' can take more
> parameters...

That does make a substantial difference. With that I would definitely
go with the chained calls to the named function, myApp.keys.register(/
* ... */).register(/* ... */) or even the most explicit

myApp.keys.register(/* ... */);
myApp.keys.register(/* ... */);

> another onRelease callback, and I'll probably add some
> kind of flags enum or something for modifier keys (still trying to
> decide how to expose that). So this:
>
>   var keys = myApp.keys;
>   keys.register
>     ('w', panNorth, endPanNorth, keys.CTRL | keys.SHIFT)
>     ('s', panSouth, endPanSouth, keys.CTRL | keys.SHIFT)
>     ('a', panWest, endPanWest, keys.CTRL | keys.SHIFT)
>     ('d', panEast, endPanEast, keys.CTRL | keys.SHIFT);
>
> ...Now becomes something like this:
>
>   var keys = myApp.keys;
>   keys.register({
>     'w': [panNorth, endPanNorth, keys.CTRL | keys.SHIFT],
>     's': [panSouth, endPanSouth, keys.CTRL | keys.SHIFT],
>     'a': [panWest, endPanWest, keys.CTRL | keys.SHIFT],
>     'd': [panEast, endPanEast, keys.CTRL | keys.SHIFT]
>   });
>
> ...Which is still pretty decent I guess. You could even make the inner
> arrays into options so you could have named parameters. Luckily the
> way I am doing it now is flexible enough to incorporate something like
> this if I decide to do it later ;)

While this is certainly possible, it starts to look ugly enough to me
that I wouldn't really want it on a public, shared API if I have any
real choice. If you're the only one using the API, then whatever you
think will remain easily readable to you after some time away from it
is fine.

>> or
>
>>   myApp.keys.register(
>>     'w', panNorth,
>>     's', panSouth,
>>     'a', panWest,
>>     'd', panEast
>>   );
>
> It would be hard to do much with that given the optional args, I
> think.

Yes, and that's the weakness of this method in general, but it's quite
nice when you don't face that possibility.

-- Scott
From: Thomas 'PointedEars' Lahn on
Lasse Reichstein Nielsen wrote:

> nick <nick___(a)fastmail.fm> writes:
>> Thomas 'PointedEars' Lahn wrote:
>>> 1. It requires you to keep an external reference to the initial calling
>>> object as Richard has already explained.
>> What about arguments.callee? That's good enough for returning a
>> reference to the same function.
>
> If performance is important, don't use "arguments" at all.
>
> "arguments.callee" always referes to the function being executed.
> You can just give that function a name and refer to it by that name.
>
> I.e.,
> DON'T: function (args) { ... arguments.callee ... }
> DO : function foo(args) { ... foo ... }

A recommendation to make maintenance harder than it needs to be while
ignoring a known bug with named function expressions in several versions of
the most widespread implementation available. DO NOT do this.


PointedEars
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
From: Thomas 'PointedEars' Lahn on
Lasse Reichstein Nielsen wrote:

> Thomas 'PointedEars' Lahn <PointedEars(a)web.de> writes:
>> 2. Several function calls will be always more expensive than just one
>> and the creation of an Array instance.
>
> That's not a given.
>
> It's possible that four function calls take longer than creating an Array
> and calling one function that loops over the array and extracts the
> elements again (possible, but not given), but the latter also allocates
> space for the array. If this happens often enough, the memory overhead
> will also cause earlier garbage collection.

That argument is dubious and the tests have no meaning because the function
argument in chaining may be an Array or Object reference anyway.
Incidentally, that is what can be observed in current general-purpose
libraries that use chaining.

You are correct that cost is not merely measured in runtime spent.
However, you are ignoring here that for, say, ten registrations I would
need ten chained function calls, or only one function call with an Array
instance of length 10. There can be no doubt that the chaining grows more
inefficient than the alternatives with each registration. With 1000
registrations I would still need only one Array instance or 1000 items to
iterate over, instead of 1000 calls to be made.

I find it particularly dubious that you have not noticed that the length of
the effective prototype chain can be an important factor regarding
efficiency. setChained() is an instance method with a short effective
prototype chain, setProp() and setProps() are prototype methods with longer
ones. I also don't see you considering that the chaining approach needs at
least one Function object more than the non-chaining one in your example.
All in all, I don't think your argument or test holds water.


PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann