Prev: Dynamicly add dropdown lists to a form populated with php/mysql query
Next: MSXML HTTP translates response status code 204 to 1223
From: kangax on 8 Sep 2009 02:27 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 8 Sep 2009 13:22 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 8 Sep 2009 14:05 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 10 Sep 2009 18:43 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 10 Sep 2009 22:02
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 |