Add reserve() to Dictionary, apply to constructors on GDScript VM#110709
Add reserve() to Dictionary, apply to constructors on GDScript VM#110709Repiteo merged 1 commit intogodotengine:masterfrom
reserve() to Dictionary, apply to constructors on GDScript VM#110709Conversation
Ivorforce
left a comment
There was a problem hiding this comment.
Great idea! reserve is especially useful for hashing types because on growth, it needs to re-hash, leading to a lot of wasted cpu cycles.
I just got one interface design comment, otherwise I think we should move forwards with this.
"Why not?" is never a good reason to add something to Core. While the code is minimal, it has no reason to be exposed to end users in the first place. It just adds another entry to the API that users will never touch. If sizing Dictionaries is a bottleneck, then you are already using the wrong data structure and no amount of calling This is exactly the sort of improvement contemplated by the first two of our contributor best practices: |
You might have understood things wrong. Dictionary is used in countless places within the engine that can benefit from this.
Except it isn't? It's only exposed for internal use within the engine where beneficial. My apologies if using the word "expose" led to wrong conclusions. I did a bit of rewording. |
|
That's my bad. I didn't notice that there wasn't any binding code touched. That takes away my concern about this being exposed to end users. Adding something like this for internal use is a lot less of a problem. That being said, if it's truly a benefit. Then this PR should be using it somewhere where dictionary allocations were a problem. Remember, the problem always needs to come first. We don't add solutions to the engine and then plan on finding problems. We start by identifying a problem, then we make changes to solve that problem. This may very well be the correct solution to a problem, but in order to merge it, we need to know what that problem is. It isn't enough to say that some places could benefit from this change. Show me what places do benefit from this change by profiling them before and after the change. |
That seems like a bit of a nonsensical take on this particular feature. I would consider this a core library feature for
You do know what that problem is. Churn in a |
There are numerous situations within the engine where known final size dictionaries could be built faster for basically free by reserving. Isn't this a problem? The point is that doing everything inside a single PR would be hard to keep track of. Take #105928 for example, which does the same thing but for The reason I did apply on the GDScript VM was mostly because that case was able to be reproduced and benchmarked within GDScript. |
a602026 to
3e17771
Compare
Sure, find one and show me. If there are numerous it should not be hard. I'm really not asking for much.
Sure, don't do everything then. Just solve the low hanging fruit issues. As it stands this PR does nothing, I am asking for it to do something before merging. |
@Shadows-of-Fire Dictionary isn't our standard map in the engine, Hashmap is. By design we don't use Dictionary in any performance sensitive areas. Which is why I said above, if we find Dictionary in a performance sensitive areas, we are likely using the wrong data structure. As I said above, if it's so obvious that this will fix a problem somewhere, then just find that problem and point it out. I'm not saying we shouldn't merge this, quite the contrary, I'm saying we should merge it after we have identified the problem it is solving and then used it to solve that problem. If the problem is so obvious, you should have no problem finding it. To be blunt, don't waste your time arguing about whether something is useful in theory. I'm certain that 'reserve()' is useful in theory. Instead, spend your time showing where it is useful 'in practice' |
|
A few cases I could find in
The one case I applied
... |
|
@clayjohn Growth is especially costly for hashing types, so In general though, while we don't use |
|
"Expose" is a very strong word in Godot, as you can see. I would suggest renaming the commit and title to something else. Maybe "Allow |
|
I'd say "Add reserve and use in GDScriptVM" |
3e17771 to
0628323
Compare
reserve() to Dictionary, use it on the GDScript VMreserve() to Dictionary, apply to constructors on GDScript VM
|
|
|
I'd keep that separately, that would only work for the C++ side due to overrides etc. so it's less critical, we don't have it for |
0628323 to
8a7a0fa
Compare
|
Rebased on top of #110717 changes. I also noticed that |
There was a problem hiding this comment.
Looks good to me, given the performance measurements provided by DeeJayLSP. But I'm not sure whether the reserve() method will be used anywhere outside of the GDScript VM and perhaps binary deserialization code (marshalls.cpp)1, given clayjohn's comment.
Footnotes
-
In the case of text deserialization, we probably don't know the dictionary size in advance ↩
clayjohn
left a comment
There was a problem hiding this comment.
My two concerns have been addressed.
I was concerned about exposing this to users, it isn't exposed to users, so that is great.
I was concerned about exposing this before it was used to optimize anything. This PR uses reserve in two places and shows a tangible benefit from using it.
So let's go ahead with this
|
Thanks! |
HashMaphas areserve()function, so I thought: why not expose it toDictionaryconsideringCowData/Vector'sreserve()is exposed toArray? There is a lot of places within the engine we could use it to reduce bottleneck.Additionally, implementing is quite simple since all needed is to call the
HashMap's ownreserve(), much like howclear()inDictionaryis mostly a wrapper to its internalHashMap'sclear().While looking for a candidate for a stress test, I noticed in the GDScript VM that the constructor for both untyped and typed
Dictionaryuses a for loop with known final size, so that was possibly the best one. The test I wrote consists in declaring aDictionarywith 63 elements mid-function 2²¹ times.Script
I made two template release builds, with and without this PR, and ran the script.
Before:
After
There are countless places where
reserve()could be used, so this is just a beginning.