"Super" hoisting

Brian Barnes ggadwa at charter.net
Fri May 13 16:21:27 UTC 2016


I can only tell you what I’ve tested and observed, not sure of the inner workings of any javascript engine.  Current alpha of my project is here:

http://www.klinksoftware.com/ws/ <http://www.klinksoftware.com/ws/>

Recommend latest firefox or chrome.  Github link for the code is there.  This is a *everything* autogenerated fps, entirely in javascript.  Performance is great, but I was getting bad performance, and it’s all tracked down to object allocation.  Because it’s 3D, I’ve made lots of classes — 3d points, vertices (includes the normal, the tangent, the bi-tangent, bone index), bones, skeletons, meshes, lights, maps, etc.  To do a lot of the math I’m always making vectors and points.  If I move all that allocation, and just re-use them in the functions, I pretty much eliminate GC pauses.

I think I keep confusing people by bad examples, it’s objects that are the problem, not primitives.  I’ll be better in the future.  I just try to carve out examples to their bare minimum.  My bad.

[>] Brian

> On May 13, 2016, at 11:47 AM, Nicolas B. Pierron <nicolas.b.pierron at mozilla.com> wrote:
> 
> I do not understand how having local variable should change any GC
> pattern unless you are creating them in the for-loop body.
> Even though, Scalar Replacement optimizations (which are implemented
> in all engine?) should be able to remove these allocations once the
> optimizing JIT is reached, unless the "setFromPoint", "rotate", and
> "average" functions are not inlined.
> 
> 
> On Fri, May 13, 2016 at 3:09 PM, Herby Vojčík <herby at mailbox.sk> wrote:
>> 
>> 
>> Brian Barnes wrote:
>>> 
>>> It could be, but there is a difference between:
>>> 
>>> var x;
>>> 
>>> and:
>>> 
>>> this.x
>> 
>> 
>> You should probably do `var x = XPool.get();` and at the end `XPool.put(x);`
>> and do `XPool`s for every possible X type which you want to not GC. And then
>> happily use local variable, as it should be. If things are not very parallel
>> / reentrant, the pools will likely only contains zero / single element.
>> 
>> 
>>> 
>>> Because you’ll get leakage from the last run, so it would probably be
>>> something that would need to be hinted, unless the engine was smart enough
>>> to reset everything it hoisted (which is doable) but not optimal, as you
>>> might always know that will be discarded (a really smart engine could
>>> probably figure this out.)
>>> 
>>> Again, all this is to try to eliminate GC as much as possible.  It’s not a
>>> philosophical argument, GC has pros and cons, it’s just bad for performance
>>> sensitive stuff like games.  I do this all by hand now, so a super hoist
>>> would just eliminate a lot of hard to read code I’ve written!
>>> 
>>> [>] Brian
>>> 
>>>> On May 13, 2016, at 10:43 AM, Alan Johnson<alan at breakrs.com>  wrote:
>>>> 
>>>> Sounds like a potential JS engine optimization, rather than a language
>>>> feature per se.
>>>> 
>>>> 
>>>>> On May 12, 2016, at 1:53 PM, Brian Barnes<ggadwa at charter.net>  wrote:
>>>>> 
>>>>> Not by my testing, at least.
>>>>> 
>>>>> My original example is a simple one to explain what the procedure is.
>>>>> Here’s a more complex example, from my code, from my mesh classes.  This
>>>>> function updates the vertices in the mesh based on the position of the bones
>>>>> of a skeleton, it gets called every frame for every model mesh that can be
>>>>> seen in the scene:
>>>>> 
>>>>>   updateVertexesToPoseAndPosition(view,skeleton,angle,position)
>>>>>   {
>>>>>       var n,v;
>>>>>       var bone,parentBone;
>>>>> 
>>>>>           // move all the vertexes
>>>>> 
>>>>>       var vIdx=0;
>>>>>       var nIdx=0;
>>>>> 
>>>>>       for (n=0;n!==this.vertexCount;n++) {
>>>>>           v=this.vertexList[n];
>>>>> 
>>>>>               // bone movement
>>>>> 
>>>>>           bone=skeleton.bones[v.boneIdx];
>>>>> 
>>>>>           this.rotVector.setFromPoint(v.vectorFromBone);
>>>>>           this.rotVector.rotate(bone.curPoseAngle);
>>>>> 
>>>>>           this.rotVector.x=bone.curPosePosition.x+this.rotVector.x;
>>>>>           this.rotVector.y=bone.curPosePosition.y+this.rotVector.y;
>>>>>           this.rotVector.z=bone.curPosePosition.z+this.rotVector.z;
>>>>> 
>>>>>           this.rotNormal.setFromPoint(v.normal);
>>>>>           this.rotNormal.rotate(bone.curPoseAngle);
>>>>> 
>>>>>               // average in any parent movement
>>>>> 
>>>>>           if (v.parentBoneIdx!==-1) {
>>>>>               parentBone=skeleton.bones[v.parentBoneIdx];
>>>>> 
>>>>> 
>>>>> this.parentRotVector.setFromPoint(v.vectorFromParentBone);
>>>>>               this.parentRotVector.rotate(parentBone.curPoseAngle);
>>>>> 
>>>>> 
>>>>> this.parentRotVector.x=parentBone.curPosePosition.x+this.parentRotVector.x;
>>>>> 
>>>>> this.parentRotVector.y=parentBone.curPosePosition.y+this.parentRotVector.y;
>>>>> 
>>>>> this.parentRotVector.z=parentBone.curPosePosition.z+this.parentRotVector.z;
>>>>> 
>>>>>               this.parentRotNormal.setFromPoint(v.normal);
>>>>>               this.parentRotNormal.rotate(parentBone.curPoseAngle);
>>>>> 
>>>>>               this.rotVector.average(this.parentRotVector);
>>>>>               this.rotNormal.average(this.parentRotNormal);
>>>>>           }
>>>>> 
>>>>>               // whole model movement
>>>>> 
>>>>>           this.rotVector.rotate(angle);
>>>>> 
>>>>>           this.drawVertices[vIdx++]=this.rotVector.x+position.x;
>>>>>           this.drawVertices[vIdx++]=this.rotVector.y+position.y;
>>>>>           this.drawVertices[vIdx++]=this.rotVector.z+position.z;
>>>>> 
>>>>>           this.rotNormal.rotate(angle);
>>>>> 
>>>>>           this.drawNormals[nIdx++]=this.rotNormal.x;
>>>>>           this.drawNormals[nIdx++]=this.rotNormal.y;
>>>>>           this.drawNormals[nIdx++]=this.rotNormal.z;
>>>>>       }
>>>>> 
>>>>>           // set the buffers
>>>>> 
>>>>>       var gl=view.gl;
>>>>>       gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexPosBuffer);
>>>>>       gl.bufferData(gl.ARRAY_BUFFER,this.drawVertices,gl.DYNAMIC_DRAW);
>>>>> 
>>>>>       gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexNormalBuffer);
>>>>>       gl.bufferData(gl.ARRAY_BUFFER,this.drawNormals,gl.STATIC_DRAW);
>>>>>   }
>>>>> 
>>>>> Notice that rotVector, rotNormal, parentRotVector, and parentRotNormal,
>>>>> are all “this”, i.e., globals.  They are *only* used in this function, no
>>>>> where else in the class.  If I put them as local variables, I get enormous
>>>>> GC pauses.  If I don’t, I get none (granted, I have to do this everywhere,
>>>>> so it’s just not this function.)
>>>>> 
>>>>> The example I gave existed only to show how it would work; not a real
>>>>> example.  Here’s a real example, that shows obvious problems with local
>>>>> variables.  Yes, these are objects.
>>>>> 
>>>>> Not that moving the objects would ONLY be something that happened at
>>>>> compile time and would be a hint; they wouldn’t be effected in the warm up
>>>>> runs.  You’d almost have to say that same hint would force a compile (which
>>>>> has it’s own problems.)
>>>>> 
>>>>> [>] Brian
>>>>> 
>>>>>> On May 12, 2016, at 1:33 PM, Allen Wirfs-Brock<allen at wirfs-brock.com>
>>>>>> wrote:
>>>>>> 
>>>>>> 
>>>>>>> On May 12, 2016, at 9:39 AM, Brian Barnes<ggadwa at charter.net>  wrote:
>>>>>>> 
>>>>>>> ...
>>>>>>> If I call doSomething a lot of times, I get a lot of objects to be
>>>>>>> tracked and GC’d.  Yes, I can rewrite that code to obviously eliminate them,
>>>>>>> but pretend there is actually something interesting happening here that
>>>>>>> requires those variables.  And engines can be smart enough to mark them and
>>>>>>> deal with them with the function ends (I don’t know if this is done.)
>>>>>>> 
>>>>>>> What I’m thinking is some kind of class based hint that would “super”
>>>>>>> hoist all local function variables.  It would be sugar but it would
>>>>>>> transform it into:
>>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> Local variables like a,b,c in your example are probably the least
>>>>>> expensive construct in JS. In your example, there are no objects created by
>>>>>> doSomething, not state that persists after doSomething  returns, and no GC
>>>>>> pressure. Anything you do WRT moving such local state into object properties
>>>>>> is going to be more expensive.
>>>>>> 
>>>>>> Allen
>>>>>> 
>>>>> _______________________________________________
>>>>> es-discuss mailing list
>>>>> es-discuss at mozilla.org
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>> 
>>> 
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>> 
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
> 
> 
> 
> -- 
> Nicolas B. Pierron

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160513/70e2e87d/attachment-0001.html>


More information about the es-discuss mailing list