Skip to content
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

Godot 4.1 performs worse than 4.0.3 (get_meta set_meta) #79222

Closed
NatGazer opened this issue Jul 8, 2023 · 13 comments
Closed

Godot 4.1 performs worse than 4.0.3 (get_meta set_meta) #79222

NatGazer opened this issue Jul 8, 2023 · 13 comments

Comments

@NatGazer
Copy link

NatGazer commented Jul 8, 2023

Godot version

Godot_v4.1-stable_mono_win64

System information

Windows 10 - Vulkan (Forward+) - RTX 3060 laptop - AMD Ryzen 5 5600H - 24GB RAM

Issue description

Godot 4.1 as a big performance loss when storing metadata in nodes (using set_meta() get_meta()). I made a demo project to test if this was the case, and it looks to be. In 4.0.3 I get around 79 FPS while on 4.1 I get around 45 FPS.

Steps to reproduce

Download the demo file and run it, it's just simple nodes and this code to stress-test the engine:

func _process(delta: float) -> void:
	for i in range(45000): 
		set_meta("a", 100.0)
		set_meta("b", "Barbacue")
		set_meta("c", 34)
		set_meta("d", 340)
		get_meta("a")
		get_meta("b")
		get_meta("c")
		get_meta("d")

Minimal reproduction project

Demo project DOWNLOAD:
403vs410.zip

@KoBeWi
Copy link
Member

KoBeWi commented Jul 9, 2023

Your test project has a 3D scene, so FPS is not an accurate measurement for a script performance...
Try using StringNames for meta (e.g. &"a"), it might speed it up.

@NatGazer
Copy link
Author

NatGazer commented Jul 9, 2023

Your test project has a 3D scene, so FPS is not an accurate measurement for a script performance...

If I deactivate the script, the 3D scene runs at 4300 fps so I'm pretty sure there's not a problem regarding 3D rendering overhead (not sure if this was your point). On top of that I'm comparing identical scenes, in different versions of godot, and I made a for loop with 45000 iterations per frame so the performance issue is very like to come from what's inside the for loop.

Try using StringNames for meta (e.g. &"a"), it might speed it up.

I'm don't know if you have read the "Steps to reproduce" section but I'm already doing that. To completely understand what's going on, I recommend you to download the file I provided and run it in 4.0.3, and then run it in 4.1. It might even be something specific about my computer, if someone could test this and see if it's getting the same results as I'm having I would appreciate. The fps counter is already integrated.

@KoBeWi
Copy link
Member

KoBeWi commented Jul 9, 2023

If I deactivate the script, the 3D scene runs at 4300 fps so I'm pretty sure there's not a problem regarding 3D rendering overhead (not sure if this was your point). On top of that I'm comparing identical scenes, in different versions of godot, and I made a for loop with 45000 iterations per frame so the performance issue is very like to come from what's inside the for loop.

Yeah, but if you detach the script, do you get the same FPS both in 4.0.3 and 4.1?

I'm don't know if you have read the "Steps to reproduce" section but I'm already doing that.

No you don't, get_meta() takes StringName as meta name, you are using plain strings. Although not sure if GDScript doesn't optimize it already.

EDIT:
Here's a better stress test:

@tool
extends EditorScript

func _run() -> void:
	var time = Time.get_ticks_msec()
	
	for i in range(45000): 
		set_meta("a", 100.0)
		set_meta("b", "Barbacue")
		set_meta("c", 34)
		set_meta("d", 340)
		get_meta("a")
		get_meta("b")
		get_meta("c")
		get_meta("d")
	
	print(Time.get_ticks_msec() - time)

The times are identical between versions.

EDIT2:
Using StringName doesn't make difference, so looks like it's automatically converted when the script is compiled.

@NatGazer
Copy link
Author

NatGazer commented Jul 9, 2023

Yeah, but if you detach the script, do you get the same FPS both in 4.0.3 and 4.1?

Yes, around 4300 fps for both versions.

No you don't, get_meta() takes StringName as meta name, you are using plain strings. Although not sure if GDScript doesn't optimize it already.

Sorry, I misunderstood your suggestion, I wasn't aware of StringNames. If I change the code to:

func _process(delta: float) -> void:
	for i in range(45000): 
		set_meta(&"a", 100.0)
		set_meta(&"b", "Barbacue")
		set_meta(&"c", 34)
		set_meta(&"d", 340)
		get_meta(&"a")
		get_meta(&"b")
		get_meta(&"c")
		get_meta(&"d")

I get exactly the same frame rates, around 45 fps for 4.03 and around 79 fps for 4.1

@KoBeWi
Copy link
Member

KoBeWi commented Jul 9, 2023

Try running the code I provided.

@AThousandShips
Copy link
Member

AThousandShips commented Jul 9, 2023

The difference here might be down to that threading changed between 4.0.3 and 4.1, meaning that there's thread guards on set/get_meta in 4.1, but not on an EditorScript

But all that aside, this stress tests things far more than could realistically be expected, it's not a real world case

@NatGazer
Copy link
Author

NatGazer commented Jul 9, 2023

I increased the iterations to 4500000 to get more consistent results:

In 4.0.3 it runs in about 1220ms:

Godot Engine v4.0.3.stable.mono.official (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.
1218
1277
1215
1224
1222
1220
1246

In 4.1 it runs in about 1350ms:

Godot Engine v4.1.stable.mono.official (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.
--- Debug adapter server started ---
--- GDScript language server started ---
1375
1354
1350
1356
1356
1352

By the way, thanks for the tip, I didn't knew I could run a code that's not attached to a node

But all that aside, this stress tests things far more than could realistically be expected, it's not a real world case

I opened this issue because this is a real problem for me. I'm creating a particle system, and I'm using metadata to store information in each particle. And if I have 200 particles in my scene, the code needs to make several metadata operations 200x200 = 40000 times per frame. I'll use C++ later and I see that metadata is slow by itself, but if this performance issue is here to improve multi-threading security I understand. Btw, I know this get's off topic but then, whats the most performant way to save variables to specific objects usign GDscript (like metadata)?

@AThousandShips
Copy link
Member

Since you said you were "stress-testing" I assumed you weren't talking about real-world cases, as they aren't really "stress-testing"

I'd say metadata isn't the way to go for that, and that you should use arrays or other storage for that, metadata isn't really intended to be performant AFAIK

@NatGazer
Copy link
Author

NatGazer commented Jul 9, 2023

So, I have an array that stores the info for each particle, but how do I link the array index with a specific particle, in other words, how do I know that a specific particle has assigned that specific index in the array? Should I change the particle name to "Pindex" like P0001, P0002, P0003, etc. hmmm, this doesn't look performant. Or maybe I could store each particle in the array itself...

@AThousandShips
Copy link
Member

I'd suggest turning to the other community channels for that

@KoBeWi
Copy link
Member

KoBeWi commented Jul 9, 2023

Note that using a separate node for each particle is already super-inefficient. Instead you could use custom drawing or RenderingServer directly. Then you can represent particles using a simple class that stores any data in plain variables, e.g.

class Particle:
    var position: Vector
    var color: Color
    var whatever

There's a nice tutorial that explains the concept, it should be possible to apply it in 3D.

@Calinou
Copy link
Member

Calinou commented Jul 9, 2023

Closing per the above comments. Some low-level functions had to be made slightly slower in 4.1 to allow other methods to be optimized (such as adding/removing a lot of nodes), as well as paving the way for SceneTree multi-threading. In practice, this should allow for a net win in most cases once SceneTree multi-threading is stable. In fact, depending on what your project currently does, the optimizations already in place in 4.1 can be a net improvement.

@NatGazer
Copy link
Author

NatGazer commented Jul 9, 2023

In fact, depending on what your project currently does, the optimizations already in place in 4.1 can be a net improvement.

Yeah, I tried to put particle logic running in the sub-thread and I got a bit more performance, but not as much as in 4.0.3. But that's because of the poor implementation I currently have as discussed before. Anyway, this opened my mind to implement this in a much faster way, and I'm planning to take advantage of multi-threading. Thank you all again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants