rich Posted August 5, 2013 Share Posted August 5, 2013 Just curious, but in the Google IO performance videos they talk about keeping the total number of object properties to around 35 or below. The key being that small well defined objects benefit from the most performance in V8, so long as they don't change shape and invalidate themselves. But I was wondering if for example you've a Sprite object, and it has a render function - but that function is just a reference to a global sprite render function held elsewhere, does it copy the render function wholesale up into the size of the object, or is it literally just a tiny reference to the other function and thus consume far less overhead? I'm especially curious to know if it applies when the object is created too (even if created for a pool I still want to know if a Sprite with a reference to a global render method is bigger/smaller than a Sprite with its own render method). Let me know if you need more details to understand what the hell I'm babbling about P.Uri.Tanner 1 Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 Here's a rough example to show what I mean: Imagine there is a sprite render function. it's got all the code needed to render the sprite to the canvas/webgl/whatever. It could be may 100 lines once you factor in all of the things an advanced renderer can do. Does it make any difference to the memory footprint / speed if this render function is part of the Sprite object itself, or if it's just stored once say in a SpriteRenderer object, and then sprite.render is just a reference to SpriteRenderer.render. I'm curious to know which has the most difference on memory / instantiation speed, or if actually there's no difference at all! Quote Link to comment Share on other sites More sharing options...
xerver Posted August 5, 2013 Share Posted August 5, 2013 Objects are never copied, only referenced. So you will hold a reference to the global function, not a copy of it. As far as speed, the difference would be negligible (if there even is one). Quote Link to comment Share on other sites More sharing options...
remvst Posted August 5, 2013 Share Posted August 5, 2013 There's a simple way of checking this:var f = Array.indexOff == Array.indexOf // returns trueBoth are referencing the same object, therefore there is only one function stored. That's why it's better to use prototype function rather than adding functions in the constructor, which should create new functions for each instance of the class (should, because there are probably optimizations to prevent this memory loss). EDIT: here is another example:function Foo(){ this.f = function(){ // blah blah }}console.log('Constructor: ' + (new Foo().f == new Foo().f)) // Returns falsefunction Bar(){}Bar.prototype.f = function(){ // blah blah}console.log('Prototype: ' + (new Bar().f == new Bar().f)) // Returns true Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 Perhaps, I'm not convinced native functions are stored in the same way. Quote Link to comment Share on other sites More sharing options...
remvst Posted August 5, 2013 Share Posted August 5, 2013 Perhaps, I'm not convinced native functions are stored in the same way.Probably, since sometimes you can't call them from a different scope. Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 Ok here's a better example, as I'm not talking about creating anonymous functions in constructors or anything like that: Method 1:function SpriteRenderer() {}SpriteRenderer.prototype.render = function(sprite) {// Lots of canvas context, transform, effects stuff going on here. Big function.}function Sprite() { this.render = SpriteRenderer.render;}Method 2:function Sprite() {}Sprite.render = function() {// The same amount of canvas context, transform, effects and such as in SpriteRenderer}The difference being that you'd only ever create 1 SpriteRenderer object, but potentially thousands of Sprite objects. Quote Link to comment Share on other sites More sharing options...
remvst Posted August 5, 2013 Share Posted August 5, 2013 I think that in terms of memory, both are equivalent. Then, in terms of modelisation, the first one is probably better Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 Yeah I'm suspecting (at least hoping!) they are actually the same internally. I guess the difference is that if you were to use Method 2 and then add a property to a Sprite for some reason (on purpose, by accident) then it will have changed its shape and I wonder if then suddenly it's no longer referring to some internal template but rather to its own copy of everything now. Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 Thanks to the power of twitter (and the awesomeness of Paul Lewis) Google engineers confirmed: @aerotwist: @photonstorm the most efficient pattern is:function foo() { do_something(); }function Obj() { this.fct = foo; }var obj1 = new Obj(); @aerotwist: @photonstorm because it's the same constructor -> same hidden class -> most optimized code (taking this straight from my convo with eng.) Quote Link to comment Share on other sites More sharing options...
sbat Posted August 5, 2013 Share Posted August 5, 2013 Rich - that's not how Google Closure library is built https://developers.google.com/closure/library/, and not in line with Google JS code guidelines http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml (at least they don't advise against single prototype inheritance). It really seems to me that "prototype" chain should be heavily optimized in V8. So method 1 should really work great. I don't see how it interferes with object shapes at all. Because from pure memory perspective "render" is not even a link on Sprite object. It is a link on Sprite's prototype object (only one instance of this guy exists). Method 2 is not directly comparable, as it creates equivalent of static functions. EDIT: There's a discussion on how prototype chain is optimized is here: https://groups.google.com/forum/#!topic/v8-users/D_rvDRx00UYMy take away from it: it is probably a little bit more efficient (from access speed point) to create function directly in the constructor. Because having a "prototype" is a little bit like inheriting your instance from "main prototype" object, and there are lookup costs for that. But from memory perspective, having function on the prototype is more efficient. And they are also optimized as part of hidden class "detection". Also, given that all industry standard libs (including Google's own) are built on prototypes, it would be weird to not look into optimizing them heavily. Quote Link to comment Share on other sites More sharing options...
Straker Posted August 5, 2013 Share Posted August 5, 2013 Rich - the problem with that code is that every object created from Obj() will contain its own reference to the foo() function. If you create a lot of sprites this way, it's unnecessary overhead. If you want to create a lot of sprites, the best way would be to use prototype. That way every object created will not have to have a separate reference to the same function. Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 It really seems to me that "prototype" chain should be heavily optimized in V8. So method 1 should really work great. I don't see how it interferes with object shapes at all. Method 2 was the one that had the potential to cause trouble with object shape changes. Method 1 was the one I'm using right now, but have never actually benchmarked to prove there was any real benefit doing it that way. Quote Link to comment Share on other sites More sharing options...
sbat Posted August 5, 2013 Share Posted August 5, 2013 Method 2 was the one that had the potential to cause trouble with object shape changes. Method 1 was the one I'm using right now, but have never actually benchmarked to prove there was any real benefit doing it that way. Method 2 does very different thing. After you do "var p = new Sprite", you cannot do p.render() in method 2. You can only do Sprite.render. So it is a rough analog of static methods. http://jsfiddle.net/MSvNR/ In method 2 you assign function as properties to constructor-function object Sprite. It does not affect instances that it produces in any way. And thus does not affect their shapes. There are really no simple alternatives for method 1. "Prototype" is the only tool to add shared bags of properties to objects in JS (and there's no differentiation between properties and functions). This works in very simple way: if JS fails to find property (such as "render") on instance itself, it checks internal (and invisible/inaccessible in any standard way) prototype link of the object and tries to find this property on the object, referenced by this invisible/internal prototype link. Non-standard __proto__ works in some browsers to check this (otherwise "secret") link. It is also special JS behavior that every object produced with Sprite constructor, will get assigned to Sprite.prototype. By specially orchestrated coincidence Function instances in Javascript have predefined "prototype" property. This property allows us to easily specify prototypes for constructors (and that's the only way to provide prototypes for objects). Note though - it has nothing to do with internal invisible/inaccessible prototype link of the function object itself (which will naturally be Function.prototype all the time). This point was particularly hard for me to grasp. Native functions are stored in exact same way. For example, it is easy to add your own function to every array in the program by doing Array.prototype.sortBackward = function() {}. After that [1, 2, 3].sortBackward will work normally. Or Function.prototype.bind. No other properties share this semantics, so Sprite.render is ignored when "new Sprite" is invoked. Only other alternative is for each object to carry full set of properties on its own (as suggested by aerotwist). Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 Here, this is closer to what I was comparing: http://jsfiddle.net/Z5Vdn/ Quite simply just the difference between fully loading all the render code within Sprite (of which there will be many objects using the same shape) or SpriteRenderer (where there will only be 1 such object in the entire game). Quote Link to comment Share on other sites More sharing options...
sbat Posted August 5, 2013 Share Posted August 5, 2013 Here, this is closer to what I was comparing: http://jsfiddle.net/Z5Vdn/ Quite simply just the difference between fully loading all the render code within Sprite (of which there will be many objects using the same shape) or SpriteRenderer (where there will only be 1 such object in the entire game). ok - that's clear. Yeah, I would agree that this.render would take more memory (1 function pointer per instance), while slightly faster for access. And this.localRender is more memory-efficient (zero per-instance memory cost, no even links to this function exist on the instance level), and slightly slower. But just to double check we are on the same page. This is totally artificial, right? console.log('SpriteRenderer.render', this) will output Sprite object instance in your code. So there's really no sense in creating it on SpriteRenderer.prototype, it would just create false impression that it belongs to SpriteRenderer instance scope. Quote Link to comment Share on other sites More sharing options...
rich Posted August 5, 2013 Author Share Posted August 5, 2013 Imagine this scenario though: Say you've got a Sprite.render method with loads of code in. You create 100 sprites that don't modify the object shape at all. Perfect, zero cost. Imagine if you then create 100 more sprites that added a property to their own instances of Sprite. They've modified the shape of Sprite, so technically I assume have just invalidated themselves. Now this is where I was really wondering what happens under the hood - would these 100 'modified' objects now have their own copy of the render function, or would it still be a reference to the original prototype (assuming the render function itself wasn't changed). Now image you create another 100 sprite objects and for some reason each one of them is given a new different property, making them all unique shapes. Have we now got another 100 copies of the render function, per object, floating around in memory? While this shouldn't actually happen in practise it's not impossible (could be an easy newbie mistake for example). So really my very original question then boiled down to wondering if with a local render function would we now have consumed loads of memory for each object shape, or is it still smart enough not to do that? Quote Link to comment Share on other sites More sharing options...
sbat Posted August 6, 2013 Share Posted August 6, 2013 EDIT: Above my speculations, here's detailed explanation on how it _really_ works: http://www.jayconrod.com/posts/52/a-tour-of-v8-object-representation. It seems my speculations were not that far off. Rich - first of all in no situations actual functions are copied. Second - Sprite.localRender function never exist on instance level. So irrespective of optimization, compiler does not need to be smart. They are simply never part of each of 1000 sprites (whether they are of the same hidden class or not). They have zero cost even on IE 6.0. Only difference is that in v8 lookup of property first on object, then on prototype will be faster, as both places can be optimized with stubs. So in method 1 (prototype inheritance), irrespective of "hidden class optimizations", 1000 instances of sprite never have even "render" function property. This is very easy to test with hasOwnProperty. It will be false for sprite.hasOwnProperty("render"). They only have link to single prototype object (Sprite.prototype). For example, if you have an object Sprite with 3 numeric properties, and then 300 methods declared on Sprite.prototype, Sprite object instances will only store 3 numeric properties plus internal hidden pointer to Sprite.prototype. Discussion thread which I quoted above suggests, that optimization is done separately for objects and prototypes. If prototype functions are called frequently, they will be wrapped into their own hidden class. Here's a quote: On Fri, Apr 16, 2010 at 5:24 PM, Anton Muhin <[email protected]> wrote: > Details are rather tricky overall, but roughly, yes, they cached: the > property is read or a function is called several times V8 looks for > its holder (object which owns the property) and compiles a stub that > first checks if any objects from receiver to holder has been changed > and if it's not the case, performs fast (sometimes really fast) > lookup. > > If you really look for all the details, take a look at src/ic.cc and > looks into {Load,Store,Call}IC_Miss functions---they eventual call > UpdateCaches which could patch the code. > > yours, > anton. Quote Link to comment Share on other sites More sharing options...
rich Posted August 6, 2013 Author Share Posted August 6, 2013 Right - bear with me, I'm struggling to find a different way to explain this All objects have fixed shapes, right? Like within V8s internal dictionary. So if you're creating hundreds of identical objects it does it really quickly. But if you start modifying the instances for whatever reason I wonder at which point the internal cache/dictionary/whereever they are held gets invalidated and has to start storing each one uniquely, because it no longer fits a dictionary definition. Quote Link to comment Share on other sites More sharing options...
rich Posted August 6, 2013 Author Share Posted August 6, 2013 And yes, it's always dangerous to try and second guess compilers. I'm just curious what they do internally when dealing with objects that reference other objects, which reference other objects, etc, etc - how far down the rabbit hole can the cache go Quote Link to comment Share on other sites More sharing options...
sbat Posted August 6, 2013 Share Posted August 6, 2013 I think what you are asking is covered in "Fast, in-object properties" in the article quoted above http://www.jayconrod.com/posts/52/a-tour-of-v8-object-representation In short and possibly wrong explanation , it creates a graph of object shapes discovered so far. And "transition" individual object between "shapes" as you add/remove properties (keeping offsets of properties that are already discovered). This is why it is important to keep initialization of variables in the same order if possible, to ensure Point will not not end up in two shapes (x, y and y, x). Even beyond constructor, it tries to do that with each individual object as you play with it. But if you do that too much and it will suspect it is creating absurd amount of shapes, it will stop and swtich the object to simple slow Dictionary. Now, related objects are not considered. They are optimized on their own. I do recommend reading the article set - it really added a lot to my understanding. For example, I was wrong to claim that functions in the approach suggested by @aerotwist will be stored on every instance. They are not. For functions V8 makes a guess that they will not change, and store them on the transition itself. If it is wrong, it creates new transition. Moreover, prototype and "own" methods use the same heuristics (constant function descriptors). Overal conclusion on functions in particular: "As with regular objects, V8 will represent prototype methods using constant function descriptors. Calling prototype methods may be a little bit slower than calling "own" methods because the compiled code has to check not only the receiver's map but also the maps of its prototype chain. This probably won't make a measurable performance difference though and shouldn't impact the way you write code." Quote Link to comment Share on other sites More sharing options...
sbat Posted August 6, 2013 Share Posted August 6, 2013 Also, please read "In-object slack tracking" part. It explain exactly how 32 properties come to play, and what does this constraint exactly mean. Quote Link to comment Share on other sites More sharing options...
Quetzacotl Posted August 6, 2013 Share Posted August 6, 2013 While memory taken by reference is not significant, I would check speed of calling local function and reference to global function.http://jsperf.com/local-function-vs-ref-to-global-function Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.