Open Ughuuu opened 2 months ago
The following commit was mode to reduce the calls to core::fmt::write
https://github.com/Ughuuu/gdext/commit/934ea4ce8f7cfe9bcecb8de8c278a1efb62ac294
This improved a lot performance. There is still bottleneck in the godot_cell::borrow_state::BorrowState::increment_shared
function. Other small things that can be improved:
class_name::to_string_name
- extra calls for thisAnother interesting issue to look at is this: https://github.com/godotengine/godot-proposals/issues/10389
I started to investigate out why the performance is so slow. On Rust version I am getting 9k circles, on new one I am getting 6k circles. So about 33% slower.
I am using Instruments app on mac which gives very good insight into what takes a lot of time from app and what functions take a lot of time. However as the result of this app is not exportable, I will either write by hand here by copy pasting or make screenshots. The following is a 5s overview of what calls take the most time from a scene where a lot of circles are created and hit the ground:
Initial Overview
We have here the main thread with
84%
usage,4.69s
out of5.55s
. The other threads:3.7%
1.7%
seem to be Rayon calling into Rapier, and there isn't much that can be done there. In these threads there is also a callback from our lib where we filter objects, however as the percentage isn't that big, our investigation won't focus on this.
That being said, we are left with the Main Thread, so lets go into that.
Note that going forward, percentages are based on the thing we are focused on. Here it's going to be based on Main Thread duration,
4.66s
.Main Thread (
4.66s
)The main thread is then split into 4 parts:
43.8%
(2.06s
) call to RapierPhysicsServer2D. (flush queries)37.5%
(1.76s
) call toRenderingServerDefault::_draw
. This is godot code. In here we have most notably vulkan functions, and nothing much to do with us.13.6%
(640ms
) call to RapierPhysicsServer2D. (step)3.6%
(173ms
) call toSceneTree::physics_process
. This is godot code. It seems to handle updating of objects to be drawn.Flow 2 and 4 don't seem to call back into our lib. So we focus on 1 and 4.
Main Thread -> Flow 3(
640ms
,13.6%
) - StepThe purpose of this function is to simulate the whole physics world with a step interval. Handles collisions and everything.
Same as before, gdext hides away the function, but our specific class calls into step function, so it's most likely that.
Here going down into the step function, it splits into:
73.1%
call into rapier lib. Here there isn't much we can do about it, aside for a callback down the linewith a small percentage is in our code as well,
handle_contact_force
:Not much to be said about this, and even if we do take this out of the equation and re-run everything, we don't see a big change in the time.
Also, seeing as the main physics process part, the
Step
function only takes this little, and the rest alone is just theFlush Queries
part, means probably the bottle neck is there.Main Thread -> Flow 1 (
2.05s
,43.8%
) - Flush QueriesThe purpose of this is to call into the
Callables
godot offer to some physics objects. These objects call these in case of collision events or active change events, etc.Notice that we have a call into RapierPhysicsServer2D, but it doesn't say specifically where. This is because of how gdext works, it seems to hide away the call. But we do see later down the call it calls into our implementation of
flush_queries
, so it's coming from thePhysicsServer2D
flush_queries
method. This method is mainly going to be calling back into godot by callingCallables
(that it previously handed down to physics objects) that had collision events or just are active.If we open this flow, we can see that we have:
69.4%
(1.43s
)Callable::callv
function. We will go into this a bit later, as the other ones are small.6.4%
(133ms
)godot_core::meta::class_name::ClassName::to_string_name
5.5%
(108ms
)godot_core::builtin::callable::Callable::bindv
- quite expensive for just binding the arguments it seems.3.6%
(76ms
)godot_core::builtin::callable::Callable::is_valid
. I like how here it usesos_unfair_lock
. Haha.2%
(54ms
)godot_core::gen::builtin_classes::array::InnerArray::push_back
Then the rest seem to be like this, keep getting smaller and smaller:
Main -> Flow1 ->
Callable::callv
(1.43s
)Going inside the Callable, it seems godot calls back into the library 5 times (from
RigidBody2D::_sync_body_state
):352ms
(27.8%
) Here we see a split intodrop_in_place
andRawGd::bind
:323ms
(25.5%
) Here most of it is fromdrop_in_place
andRawGd::bind
:199ms
(15.7%
) Same as previous call,drop_in_place
andRawGd::bind
168ms
(13.3%
) Same164ms
(12.9%
) Same