Closure vs Rewrite

T

Thomas 'PointedEars' Lahn

Jorge said:
The time to create it is === the time to create it,
Correct.

no matter *when*.

Nonsense, see above.
What matters is that if it isn't needed, why bother creating it ?

What matters is that it takes extra effort to recognize if it is needed.
And that effort takes time. So it matters very much when creation takes
place, and under which conditions.
How do you know that it takes additional "parsing" time ?

As the reference needs to be searched for, that much is self-evident.
You provide another example:
How could you possibly detect while parsing a, for example, args= eval
('arg'+'uments'); ?

That's in fact the beginning of a really good argument against conditional
creation of `arguments'.
What if it's a call to its getter what triggers its creation
"lazily" ?

That would be an impossible implementation.
Have you ever heard about the "lazy-function-definition-pattern" ? :)

You are not making any sense.


PointedEars
 
M

Mano

That's in fact the beginning of a really good argument against conditional
creation of `arguments'.

No. That's in fact a really good argument against your theory that
arguments' use is detected while parsing.
That would be an impossible implementation.

Because... ?
 
J

Jorge

That's in fact the beginning of a really good argument against conditional
creation of `arguments'.


No. That's in fact a really good argument against your theory that
arguments' use is detected while parsing.
That would be an impossible implementation.

Because... what ?
 
J

Jorge

Jorge wrote:

[...]
My 2 cents:
var func_yetAnother= function f () {
  if (f.flag) { return f.x; }
  f.flag= true;
  return (f.x= expensive());
  };
Allows returning falsy values, runs almost as fast -if not faster- and
no closures are created:

I don't see a need for "flag" here.

function fn() {
   if ('cached' in fn) {
     return fn.cached;
   }
   return (fn.cached = expensive());

};

or even slightly more concise (or cryptic):

function fn() {
   return ('cached' in fn)
     ? fn.cached
     : (fn.cached = expensive());

};

Nice. Just one property. But a bit slower: it seems that 'if ("cached"
in f)' is a bit (a lot in Safari/Mac) slower than an 'if (f.flag)':
It's also a good idea to understand that assigning to a property of a
function has a potential to "leak" information that should not be
exposed. Closure provides such "privacy" (except in environments like
SpiderMonkey, which allow `eval` to be called in an arbitrary scope -
something that's considered a bug by Mozilla)

Yes: <http://javascript.crockford.com/private.html>
 
T

Thomas 'PointedEars' Lahn

Mano wrote:
^^^^[1]
No. That's in fact a really good argument against your theory that
arguments' use is detected while parsing.

That is _not_ my theory, you misunderstood. I said instead that the
possibility that your example demonstrates, points out how impractical it
in fact is that the `arguments' object is created only if it is being
referred to in the source code. For there can be, obviously, any number of
references (including yours) that are _not_ obvious from parsing the source
code.

But, on the other hand, if you do _not_ parse the source code when control
enters the local execution context, you have to wait for the reference to
be resolved, and that would slow down execution as it is not supposed to be
resolved before execution reaches the corresponding statement. That is, if
constructing the `arguments' object really was as expensive as was assumed
by those who use that assumption as an argument in favor of conditional
creation of the object.
Because... ?

An object cannot have a getter before it was created. (That was easy!)

Who? [^1]


PointedEars
 
J

Jorge

An object cannot have a getter before it was created.  (That was easy!)

Of course. But it's cheaper (read: faster) to create a (dummy, but not
non-existent) arguments object that only initializes itself (read: the
expensive operation) "lazily": if/only when its getter is called.
Who? [^1]

(Manuel === Mano)-Jorge.
 
T

Thomas 'PointedEars' Lahn

Jorge said:
Of course. But it's cheaper (read: faster) to create a (dummy, but not
non-existent) arguments object that only initializes itself (read: the
expensive operation) "lazily": if/only when its getter is called.

Apparently you forget that this would require additional code to check if
the object was already initialized. Looks like it gets less efficient with
every new idea you make up to make it appear more efficient.

Nice try, though.


PointedEars
 
L

Lasse Reichstein Nielsen

Mano said:
No. That's in fact a really good argument against your theory that
arguments' use is detected while parsing.

Uhm, no.

While parsing, you can detect usage of an "arguments" variable in a
function's scope. If you are certain that it isn't used, you can
optimize the function call. You don't have to be certain that it is
used (which is indeed undecidable in general), since it's just an
optimization. You just have to have sufficient cases where you know
for certain that you can optimize.

Reasons to not be certain include the function using the eval function
(or using it unaliased, for implementations that don't allow aliased
eval to change the local scope).

Luckily, almost no functions use "arguments" or "eval", and all calls
to all these functions can be optimized.

/L
 
L

Lasse Reichstein Nielsen

kangax said:
Jorge wrote:

[...]
....
Also, using `in` is obviously not the best solution when working with
host methods (as it might not be a good idea to augment them with
"custom" properties).

If we are doing this, and mind you - I don't necessarily think it's
worth doing, then I would do something like:

var fn = (function() { var cache;
return function () {
return cache || (cache = expensive());
;} })();

/L
 
T

Thomas 'PointedEars' Lahn

Lasse said:
Luckily, almost no functions use "arguments" or "eval", and all calls
to all these functions can be optimized.

You seem to continue missing that the "optimization" requires *additional*
code which makes the whole thing more expensive, not less.


PointedEars
 
J

Jorge

If we are doing this, and mind you - I don't necessarily think it's
worth doing, then I would do something like:

var fn = (function() { var cache;
                       return function () {
                         return cache || (cache= expensive());
                       ;} })();

This can't return falsy (cached) values...
 
J

Jorge

Uhm, no.

While parsing, you can detect usage of an "arguments" variable in a
function's scope. If you are certain that it isn't used, you can
optimize the function call. You don't have to be certain that it is
used (which is indeed undecidable in general), since it's just an
optimization. You just have to have sufficient cases where you know
for certain that you can optimize.

Reasons to not be certain include the function using the eval function
(or using it unaliased, for implementations that don't allow aliased
eval to change the local scope).

Luckily, almost no functions use "arguments" or "eval", and all calls
to all these functions can be optimized.

Yes, you seem to be right: the simple presence of an 'eval' or an
'arguments' in the source slows it down:

var func_yetAnother = function f () {
if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());
};

var func_yetAnotherDummyEval = function f (p) {
if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());
if (0) { eval(p); }
};

var func_yetAnotherDummyArgs = function f () {
if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());
if (0) { arguments; }
};

<http://jorgechamorro.com/cljs/030/>
 
T

Thomas 'PointedEars' Lahn

The first `;' seems misplaced (although syntactically valid); should be

};
})();
This can't return falsy (cached) values...

return (typeof cache != "undefined") ? cache : (cache = expensive());

can, if we assume that expensive() never returns `undefined'.


PointedEars
 
L

Lasse Reichstein Nielsen

Thomas 'PointedEars' Lahn said:
You seem to continue missing that the "optimization" requires *additional*
code which makes the whole thing more expensive, not less.

And you are missing that you can save creating an object, with
properties linked to the parameter variables of the function, for 95%+
of all function calls. That is a significant save, for a fairly simple
and cheap static analysis.

/L
 
T

Thomas 'PointedEars' Lahn

kangax said:
Thomas 'PointedEars' Lahn wrote:
[...]
return (typeof cache != "undefined") ? cache : (cache = expensive());

can, if we assume that expensive() never returns `undefined'.

What about using unique "key"?

var fn = (function() {
var cache = KEY = { };
return function () {
return (cache != KEY)
? cache
: (cache = expensive());
}
})();

Creates another closure, but I like it nevertheless.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Lasse said:
And you are missing that you can save creating an object, with
properties linked to the parameter variables of the function, for 95%+
of all function calls. That is a significant save, for a fairly simple ^^^^^^^^^^^^^^^
and cheap static analysis.
^^^^^^^^^^^^^^^^^^^^^^^^^

I have to see that before I can believe it.


PointedEars
 
J

Jorge

Thomas 'PointedEars' Lahn wrote:

[...]
  return (typeof cache != "undefined") ? cache : (cache = expensive());
can, if we assume that expensive() never returns `undefined'.

What about using unique "key"?

var fn = (function() {
   var cache = KEY = { };
   return function () {
     return (cache != KEY)
       ? cache
       : (cache = expensive());
   }

})();

Nice. Yet another closure, but nice. Lets see how it compares to
"yetAnother2" (yours is "kangaxThree") :

var func_kangaxThree = (function() {
var cache = KEY = { };
return function () {
return (cache != KEY)
? cache
: (cache = expensive());
}
})();

var func_yetAnother2 = (function() {
var cache, key;
return function () {
if (key) { return cache; }
key= true;
cache = expensive();
}
})();

<http://jorgechamorro.com/cljs/030/>

And the winner is... yetAnother2 ! (even faster than yetAnother !)

Thanks,
 
T

Thomas 'PointedEars' Lahn

Jorge said:
[...] Lets see how it compares to "yetAnother2" (yours is "kangaxThree") :

var func_kangaxThree = (function() {
var cache = KEY = { };
return function () {
return (cache != KEY)
? cache
: (cache = expensive());
}
})();

var func_yetAnother2 = (function() {
var cache, key;
return function () {
if (key) { return cache; }
key= true;
cache = expensive();
}
})();

<http://jorgechamorro.com/cljs/030/>

And the winner is... yetAnother2 ! (even faster than yetAnother !)

*Maybe* (JSLitmus looks rather dubious), it wins in run time. But what
about memory? The target of the exercise was to *reduce* the number of
bound variables, wasn't it?


PointedEars
 
T

Thomas 'PointedEars' Lahn

kangax said:
Thomas 'PointedEars' Lahn wrote:
[...]
*Maybe* (JSLitmus looks rather dubious), it wins in run time. But what
about memory? The target of the exercise was to *reduce* the number of
bound variables, wasn't it?

Well, considering loose nature of `undefined` value in Javascript, the
only way (I see) to eliminate additional references is to use `in` (or
perhaps `hasOwnProperty`, although latter one might be an overkill).

Also, the standalone `in' operation requires at least JavaScript 1.5,
JScript 5.0, ECMAScript Ed. 3, and Object.prototype.hasOwnProperty()
requires at least JavaScript 1.5, JScript 5.5, ECMAScript Ed. 3.
Both would allow to know whether an operation has been actually
performed and both don't depend on the returned value.

I don't think having one more reference to a `cache` object (i.e. `KEY`
identifier in my previous example) is a big deal.

ACK. I was only to point out that runtime efficiency should not be a single
goal; it always has to be balanced against memory efficiency, and
vice-versa.
I would use it as an alternative to `in` when function augmentation is
not safe/desirable.

When is function augmentation ever not safe/desirable?
It's also worth mentioning that `expensive` function can mess things up
by simply returning `KEY` (which would lead to enclosing function
getting into an `expensive` block again). The `KEY` example, therefore,
is not reliable in cases when `expensive` can not be trusted (as it can
brake program integrity).

Fortunately, you are mistaken here. The scope of local variables is not
automatically extended to called subroutines:

function expensive()
{
return KEY;
}

function x()
{
var KEY = {};
return expensive();
}

// ReferenceError: KEY is not defined
x();

Probably you have confused this with closures:

function x()
{
var KEY = {};
return (function() {
return KEY;
})();
}

However, such a closure must be considered trusted code, as you wrote it
yourself.


PointedEars
 
T

Thomas 'PointedEars' Lahn

kangax said:
Thomas 'PointedEars' Lahn wrote:
[...]
Also, the standalone `in' operation requires at least JavaScript 1.5,
JScript 5.0, ECMAScript Ed. 3, and Object.prototype.hasOwnProperty()
requires at least JavaScript 1.5, JScript 5.5, ECMAScript Ed. 3.

Good to know.
IIRC, `hasOwnProperty` is also not implemented in Safari <=2.0.2

Thanks, the Matrix has you ;-)


Regards,

PointedEars
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
DewittMill
Top