From: kangax on
Thomas 'PointedEars' Lahn wrote:
> abozhilov wrote:
>> On Sep 7, 2:20 am, Jorge <jo...(a)jorgechamorro.com> wrote:
>>> On Sep 6, 10:12 pm, abozhilov <fort...(a)gmail.com> wrote:
>>>> (...)
>>>> alert(Object.prototype.toString.call(bar));
>>> === alert( ({}).toString.call(bar) );
[...]
>> This is exactly the same like:
>> Object.prototype.toString.call(bar);
>
> No. At least in JavaScript 1.8.1 as of Firefox/Iceweasel 3.5.1, by contrast
> the Object Initializer creates a new object using the original Object
> constructor, regardless of the value that `Object' has been assigned. (ES3F
> is ambiguous here in section 11.1.5, it says "Create a new object as if by
> the expression `new Object'" without saying whether `Object' means the
> original value; apparently, the Mozilla.org people thought of it as meaning
> the original value.)
>
> Test case:
>
> Object = function() {
> this.bar = 42;
> };
>
> /*
> * in JavaScript 1.8.1:
> * string representation of the value assigned before
> */
> String(Object)
>
> /* in JavaScript 1.8.1: "undefined" */
> typeof {}.bar
>
> In addition, your approach uses three lookup operations and two calls
> through the call() wrapper, while Jorge's approach uses only two lookup
> operations and two calls.

and a (probably) minor overhead of `{}` evaluation (which is what
grouping operator in Jorge's example does) :)

>
> It remains to be seen which approach is more memory-efficient, and which one
> more runtime-efficient; I would assume that Jorge's approach is more
> runtime-efficient (as more of the work would be done internally), but less
> memory-efficient (as a new object is created each time) than yours.

I would never believe that creation of a new object (even the one as
simple as Object object) would be more runtime-efficient than 1 property
lookup (3 in `Object.prototype.toString.call` - 2 in `({}).toString.call`)

A quick test in FF3.5.2, for example, shows:

var o = { }, lim = 100000;

console.time(1);
for (var i=lim; i--; ) Object.prototype.toString.call(o);
console.timeEnd(1);

console.time(2);
for (var i=lim; i--; ) ({}).toString.call(o);
console.timeEnd(2);

1: 183ms
2: 278ms

And of course, as you said, there's a memory consumption overhead, which
should actually directly affect runtime-performance as well. From what
I've seen, every time garbage collector kicks in, there's a runtime
performance hit.

>
> There is a third approach that tries to get the best of both worlds: Create
> and initialize the "empty" Object object only once, and reuse it later.

Indeed.

var _toString = ({}).toString;
function foo(o){ return _toString.call(o); }

1 lookup, 1 function call, no object creation at runtime.

[...]

--
kangax
From: Garrett Smith on
kangax wrote:
> Thomas 'PointedEars' Lahn wrote:
>> abozhilov wrote:
>>> On Sep 7, 2:20 am, Jorge <jo...(a)jorgechamorro.com> wrote:
>>>> On Sep 6, 10:12 pm, abozhilov <fort...(a)gmail.com> wrote:
>>>>> (...)
>>>>> alert(Object.prototype.toString.call(bar));
>>>> === alert( ({}).toString.call(bar) );
> [...]

[...]

> A quick test in FF3.5.2, for example, shows:
>
> var o = { }, lim = 100000;
>
> console.time(1);
> for (var i=lim; i--; ) Object.prototype.toString.call(o);
> console.timeEnd(1);
>
> console.time(2);
> for (var i=lim; i--; ) ({}).toString.call(o);
> console.timeEnd(2);
>
> 1: 183ms
> 2: 278ms
>

Using a new file, with that code in a SCRIPT tag:
1: 26ms
2: 35ms

Object.prototype.toString.call is faster.

Firebug execution is not the same as global execution context.
Performance is affected. Firebug code is interpreted as differently, too:-

a();
function a(){return 1;}

ReferenceError: a is not defined

The same error is produced if |function a| is interepreted as a
FunctionStatement (Spidermonkey extension). We can test this by placing
|function a| in a block:-

{
a();
function a(){return 1;}
}

ReferenceError: a is not defined

So whatever Firebug is doing with your code, it is not running it as
ProgramCode.

A Firebug quick test (not really a test) could be made less skewed by
having the entire test wrapped in a function, so it runs as
FunctionCode, declaring |Object| in the same scope, to mitigatewhatever
Firebug does with the scope chain, plus the function introduced by the
test.

Running in Firebug:

(function(){
var o = { }, lim = 100000,
Object = self.Object,
toString = ({}).toString;

console.time(1);
for (var i=lim; i--; ) Object.prototype.toString.call(o);
console.timeEnd(1);

console.time(2);
for (var i=lim; i--; ) ({}).toString.call(o);
console.timeEnd(2);

console.time(3);
for (var i=lim; i--; ) toString.call(o);
console.timeEnd(3);
})();

1: 28ms
2: 36ms
3: 26ms

Object.prototype.toString.call is faster, and fastest of course is
having no property lookup in the call to |toString|, but that is a
negligible difference and only 2ms a 100k loop.

> And of course, as you said, there's a memory consumption overhead, which
> should actually directly affect runtime-performance as well. From what
> I've seen, every time garbage collector kicks in, there's a runtime
> performance hit.
>
>>
>> There is a third approach that tries to get the best of both worlds:
>> Create
>> and initialize the "empty" Object object only once, and reuse it later.
>
> Indeed.
>
> var _toString = ({}).toString;
> function foo(o){ return _toString.call(o); }
>
> 1 lookup, 1 function call, no object creation at runtime.
>

Anything done in a loop should be optimized, as should any code that is
called frequently (mousemove, setInterval).

Recursive calls, too, but sometimes a recursive function can be written
clearly using a loop construct, eliminating the extra function calls and
contexts.

Garrett
--
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: kangax on
Garrett Smith wrote:
> kangax wrote:
>> Thomas 'PointedEars' Lahn wrote:
>>> abozhilov wrote:
>>>> On Sep 7, 2:20 am, Jorge <jo...(a)jorgechamorro.com> wrote:
>>>>> On Sep 6, 10:12 pm, abozhilov <fort...(a)gmail.com> wrote:
>>>>>> (...)
>>>>>> alert(Object.prototype.toString.call(bar));
>>>>> === alert( ({}).toString.call(bar) );
>> [...]
>
> [...]
>
>> A quick test in FF3.5.2, for example, shows:
>>
>> var o = { }, lim = 100000;
>>
>> console.time(1);
>> for (var i=lim; i--; ) Object.prototype.toString.call(o);
>> console.timeEnd(1);
>>
>> console.time(2);
>> for (var i=lim; i--; ) ({}).toString.call(o);
>> console.timeEnd(2);
>>
>> 1: 183ms
>> 2: 278ms
>>
>
> Using a new file, with that code in a SCRIPT tag:
> 1: 26ms
> 2: 35ms
>
> Object.prototype.toString.call is faster.
>
> Firebug execution is not the same as global execution context.
> Performance is affected. Firebug code is interpreted as differently, too:-
>
> a();
> function a(){return 1;}
>
> ReferenceError: a is not defined
>
> The same error is produced if |function a| is interepreted as a
> FunctionStatement (Spidermonkey extension). We can test this by placing
> |function a| in a block:-
>
> {
> a();
> function a(){return 1;}
> }
>
> ReferenceError: a is not defined
>
> So whatever Firebug is doing with your code, it is not running it as
> ProgramCode.

It's not. It appears to be executed as Eval Code in a global scope via
`window.eval` with all its consequences (e.g. variable declarations
resulting in properties without DontEnum). It's interesting that
`window.eval` doesn't set `thisArg` to reference `window`, and so `this`
in console references firebug's `_FirebugCommandLine`.

From what I remember `window.eval(/*...*/)` in Gecko is similar to -
`with (window) eval(/*...*/)`. Besides that, Firebug seems to import its
helpers by doing - `with (_FirebugCommandLine) { /*...*/ }`, which would
explain all these slowdowns.

It also leaks some of its housekeeping details:

typeof expr; // "string"

>
> A Firebug quick test (not really a test) could be made less skewed by
> having the entire test wrapped in a function, so it runs as
> FunctionCode, declaring |Object| in the same scope, to mitigatewhatever
> Firebug does with the scope chain, plus the function introduced by the
> test.
>
> Running in Firebug:
>
> (function(){
> var o = { }, lim = 100000,
> Object = self.Object,
> toString = ({}).toString;
>
> console.time(1);
> for (var i=lim; i--; ) Object.prototype.toString.call(o);
> console.timeEnd(1);
>
> console.time(2);
> for (var i=lim; i--; ) ({}).toString.call(o);
> console.timeEnd(2);
>
> console.time(3);
> for (var i=lim; i--; ) toString.call(o);
> console.timeEnd(3);
> })();
>
> 1: 28ms
> 2: 36ms
> 3: 26ms

Thanks for catching that. I'll be sure to wrap tests with functions from
now on (or just use plain page/script tests).

[...]

--
kangax
From: Garrett Smith on
kangax wrote:
> Garrett Smith wrote:
>> kangax wrote:
>>> Thomas 'PointedEars' Lahn wrote:
>>>> abozhilov wrote:
>>>>> On Sep 7, 2:20 am, Jorge <jo...(a)jorgechamorro.com> wrote:
>>>>>> On Sep 6, 10:12 pm, abozhilov <fort...(a)gmail.com> wrote:
>>>>>>> (...)
>>>>>>>
[...]

>>
>> Firebug execution is not the same as global execution context.
>> Performance is affected. Firebug code is interpreted as differently,
>> too:-
>>
>> a();
>> function a(){return 1;}
>>
>> ReferenceError: a is not defined
>>

Valid program code.

>> The same error is produced if |function a| is interepreted as a
>> FunctionStatement (Spidermonkey extension). We can test this by
>> placing |function a| in a block:-
>>
>> {
>> a();
>> function a(){return 1;}
>> }
>>
>> ReferenceError: a is not defined
>>
>> So whatever Firebug is doing with your code, it is not running it as
>> ProgramCode.
>
> It's not. It appears to be executed as Eval Code

How could that be? eval must parse the string value as a Program. Here's
a perfectly valid program:-

x();
function x(){ console.log('done x!'); }

Maybe it's going line by line.

in a global scope via
> `window.eval` with all its consequences (e.g. variable declarations
> resulting in properties without DontEnum). It's interesting that
> `window.eval` doesn't set `thisArg` to reference `window`, and so `this`
> in console references firebug's `_FirebugCommandLine`.
>
> From what I remember `window.eval(/*...*/)` in Gecko is similar to -
> `with (window) eval(/*...*/)`. Besides that, Firebug seems to import its
> helpers by doing - `with (_FirebugCommandLine) { /*...*/ }`, which would
> explain all these slowdowns.
>
> It also leaks some of its housekeeping details:
>
> typeof expr; // "string"

Uh, not good.

I also noticed a |window.f| property doing in the DOM tab.

I have been checking that DOM tab after running my tests and it has
helped me find a couple of undeclared identifiers.

1) run test
2) look at DOM tab
3) check for things like |i|, or other identifiers.

Garrett
--
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: kangax on
Garrett Smith wrote:
> kangax wrote:
>> Garrett Smith wrote:
[...]
>>> The same error is produced if |function a| is interepreted as a
>>> FunctionStatement (Spidermonkey extension). We can test this by
>>> placing |function a| in a block:-
>>>
>>> {
>>> a();
>>> function a(){return 1;}
>>> }
>>>
>>> ReferenceError: a is not defined
>>>
>>> So whatever Firebug is doing with your code, it is not running it as
>>> ProgramCode.
>>
>> It's not. It appears to be executed as Eval Code
>
> How could that be? eval must parse the string value as a Program. Here's
> a perfectly valid program:-
>
> x();
> function x(){ console.log('done x!'); }
>
> Maybe it's going line by line.

Maybe. I have no idea (need to look at FB's source).

>
> in a global scope via
>> `window.eval` with all its consequences (e.g. variable declarations
>> resulting in properties without DontEnum). It's interesting that
>> `window.eval` doesn't set `thisArg` to reference `window`, and so
>> `this` in console references firebug's `_FirebugCommandLine`.
>>
>> From what I remember `window.eval(/*...*/)` in Gecko is similar to -
>> `with (window) eval(/*...*/)`. Besides that, Firebug seems to import
>> its helpers by doing - `with (_FirebugCommandLine) { /*...*/ }`, which
>> would explain all these slowdowns.
>>
>> It also leaks some of its housekeeping details:
>>
>> typeof expr; // "string"
>
> Uh, not good.
>
> I also noticed a |window.f| property doing in the DOM tab.

I don't see `window.f` in 1.5X.0a23 (that's one weird version/build
number). Are you sure it wasn't declared by some other script?

>
> I have been checking that DOM tab after running my tests and it has
> helped me find a couple of undeclared identifiers.
>
> 1) run test
> 2) look at DOM tab
> 3) check for things like |i|, or other identifiers.

I have a bookmarklet for that :)
<http://thinkweb2.com/projects/prototype/detecting-global-variable-leaks/>

--
kangax