-
-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
reduce memory allocation in component update core codepath #3772
Conversation
9ae6222
to
8af4a5b
Compare
8af4a5b
to
5052818
Compare
Ready to go. Did another test with the /performance/animation-set-attribute stress test to measure memory. These recordings were taken over 4 seconds. We save the following allocation calls:
With the particular stress test, it does lose out on ~5FPS (20FPS vs 15FPS), but it's also an unrealistic example since we wouldn't use setAttributes that hard and often for animations. Dodging GC is more important because GC will lose you entire frames. We could perhaps have an option for components to not do any diffing, cloning, comparing, and just always run the update method no matter what, and let the component sort out data changes. We are probably losing a lot of time making sure redundant calls don't get processed. BeforeAfter |
@ngokevin i would like test this change, how do you generate the memory statistics? |
src/components/camera.js
Outdated
@@ -79,6 +79,11 @@ module.exports.Component = registerComponent('camera', { | |||
// Camera disabled. Set camera to another camera. | |||
system.disableSpectatorCamera(); | |||
} | |||
|
|||
this.savedPose = { | |||
position: el.getAttribute('position'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have to allocate new objects here. You will be holding a reference to the entity shared objects that will change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just removed it, it wasn't used.
browser memory tools |
1e9660e
to
784aafb
Compare
It would be nicer if this PR could |
@@ -21,12 +21,16 @@ module.exports.Component = registerComponent('obj-model', { | |||
update: function () { | |||
var data = this.data; | |||
if (!data.obj) { return; } | |||
this.remove(); | |||
this.resetMesh(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to the purpose of the PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixes a test
var shader = newShader || currentShader; | ||
var schema = shaders[shader] && shaders[shader].schema; | ||
var currentShader; | ||
var newShader; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated to PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixes test, checks for data / oldData existence
*/ | ||
updateSchema: function (data) { | ||
var currentGeometryType = this.oldData && this.oldData.primitive; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated to PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it checks for oldData rather than data
styleParser.js and component.js changes could be separate PRs |
It's hard and effort to break up. They both relate to the object pooling and both tie together. There's only two major changes:
I detail listed all the changes in the original description. It touches lots of files because it's a change deep in the core of A-Frame. |
this breaks my little app with body.js:122 Uncaught TypeError: Cannot read property 'toUpperCase' of undefined at initBody (body.js:122) at HTMLElement. (a-node.js:300) at a-node.js:131 initBody @ body.js:122 emit @ a-node.js:300 (anonymous) @ a-node.js:131 Promise.then (async) load @ a-node.js:128 load @ a-entity.js:255 (anonymous) @ a-scene.js:591 setTimeout (async) play @ a-scene.js:590 attachedCallbackPostCamera @ a-scene.js:128 (anonymous) @ a-scene.js:102 emit @ a-node.js:300 checkUserCamera @ camera.js:98 (anonymous) @ camera.js:58 emit @ a-node.js:300 setObject3D @ a-entity.js:166 init @ camera.js:28 updateProperties @ component.js:315 updateComponent @ a-entity.js:489 updateComponents @ a-entity.js:463 (anonymous) @ a-entity.js:259 (anonymous) @ a-node.js:130 Promise.then (async) load @ a-node.js:128 load @ a-entity.js:255 (anonymous) @ a-entity.js:84 emit @ a-node.js:300 (anonymous) @ a-node.js:131 Promise.then (async) load @ a-node.js:128 load @ a-assets.js:83 (anonymous) @ a-assets.js:70 setTimeout (async) value @ a-assets.js:66 |
can you log this.data, and more information if you're updating it, initializing it...a live example would be great |
here is a live example https://codesandbox.io/s/5k1wxylxkx |
Thanks, possible to have one with the physics as an unminified project file so I can poke around in it? |
https://codesandbox.io/s/vv6jm1l06l with unminified physics |
@ngokevin should i open a new bugreport? |
thanks sure |
Description:
Rather than create a new object each time to create a new
oldData
, just have two objects representingoldData
anddata
. Also when updating, rather than create a newdata
object, have anewData
object that is used for holding the new data which then gets copied intodata
. So in total, only use three objects:oldData
,data
,newData
.Changes proposed:
style-attr
implementation to be able to pass in an object for reuse instead of creating a new one each time. Also remove lots of callbacks,Object.keys()
array creations from their code. Also tweaked to do prettier stringifies. We manage this forked dependency now.oldData
.oldData
.buildData
for default value copying.buildData
, was usingObject.keys
.isSingleProp
,isSinglePropObject
,isObjectBased
)styleParser
utils a bit to not allocate object + array + use callbacks to convert keys to camelCase, and to cache regexes.Updated to not delete the keys when recycling an object, but just set every key to
undefined
. This means object pooling best used when objects share a common schema, in case we iterate over keys. So I create different object pools for each type of component, since their object keys will be the same. I also update schema parser to ignore undefined keys.Before
After
Improvements: See the
updateProperties
andupdateCachedAttrValue
no longer on the list of frequent memory allocators.