-
-
Notifications
You must be signed in to change notification settings - Fork 78
Mitigate JavaScript Object Allocation for Performance Gain (Phase 1) #337
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
base: master
Are you sure you want to change the base?
Mitigate JavaScript Object Allocation for Performance Gain (Phase 1) #337
Conversation
|
Just to give an update--I am continuing work on this PR this weekend. |
…t-creation-2 # Conflicts: # src.ts/dynamics/rigid_body.ts # src/dynamics/rigid_body.rs
… into feat/mitigate-object-creation-2
|
@ThierryBerger @sebcrozet Thierry/Seb, I have taken this PR out of draft mode and would like to know if we can merge what I have currently into the master branch. I would like to finish the remaining work in a separate "Phase 2" PR. You might notice two methods in this PR called Note: the |
December 19, 2025 Update:
I am taking this PR out of draft mode and renaming it "Phase 1".
The reason for this is that I would like to see if the current changes do not cause any regression.
I will submit a "Phase 2" PR to finish the remaining work required to remove all the object allocations.
Completion status:
✅ rigid_body
✅ toi
🕒 narrow_phase
// <-- Phase 2🕒 character_controller
// <-- Phase 2🕒 ray_cast_vehicle_controller
// <-- Phase 2✅ collider
🕒 multibody_joint
// <-- Phase 2🕒 impulse_joint
// <-- Phase 2🕒 contact
// <-- Phase 2🕒 ray
// <-- Phase 2🕒 point
// <-- Phase 2🕒 pid_controller
// <-- Phase 2🕒 shape
// <-- Phase 2This PR is a spiritual continuation of #25 , except we are also now refactoring the Rust side of things, so it is possible to allocate 0 JS objects per call as opposed to 1.
Currently, every time
rigidBody.translation()is called, 2 objects are created in JavaScript that have to be garbage collected. This goes forrigidBody.rotation()as well and all the other methods that return objects to JavaScript.Here is the
translation()method:rapier.js/src.ts/dynamics/rigid_body.ts
Lines 297 to 300 in 4d3b651
this.rawSet.rbTranslation(this.handle)creates a newRawVectorobject.VectorOps.fromRaw(res)creates a newRapier.Vector3object by essentially cloning the firstRawVectorobject.In any 3D world you use Rapier.js with, you will need to call
translation()androtation()for each rigid body on every frame in order to synchronize your rendered objects (e.g. THREE.js meshes) to Rapier.js.So, in a hypothetical world with 1,000 rigid bodies running at 60fps, this is equivalent to 60,000 calls to
translation()and 60,000 calls torotation()per second. Furthermore, since these methods internally create 2 objects each, this is equivalent to 60,000 * 2 * 2 = 240,000 objects/second that need to be garbage collected, and this is ONLY if you calltranslation()androtation()once for each rigid body per frame. If you call the methods multiple times for each rigid body per frame, the object creation can easily reach millions per second.In this pull request, I have changed the implementation for these methods to give the user the option of providing their own pre-allocated "target" object, into which x/y/z values will be copied. I have also changed the Rust counterparts to these methods to NOT return
RawVectorobjects, but rather to take ascratchBufferobject in the form of aFloat32Arrayso that Rust values can be copied there for JavaScript to read from.RigidBody.translation()now looks like this:RigidBody (rigid_body.ts)
VectorOps (math.ts)
RawRigidBodySet (rigid_body.rs)
The user can utilize this new API like this. In the following example, only one object is allocated, even though we called
translation()five times:Performance Study
Here are the performance gains for 1,000 rigid bodies simulated at 60 FPS with the new API:
Anecdotally, I have also experienced a great reduction in GC jank in Firefox especially, aside from the FPS gain.
Run the benchmark yourself here: https://jsfiddle.net/qyg058wd