Core: Add initial architecture for first-class Object types. Optimize is_class#105793
Conversation
f522cdc to
4876091
Compare
4876091 to
c5021b1
Compare
|
Alternative just off top of my head, don't know if this is doable:
That should be a hashtable lookup, and a bit check. Essentially I'm wondering if the |
Indirection is also a notable concern: The current system identifies each class uniquely with a The main reason I want to get rid of this extensive |
Yup, sure, e.g.
Ah maybe that would be the clincher, I didn't realize we had so many GDCLASSes. 😁 But maybe as we mentioned a while back, we could have a parallel simple faster bit check available for classes derived from |
Agreed, that's definitely worth checking for performance benefits! |
|
Just a little update, I was able to use In the end, it makes more sense to build around and optimize existing systems than to introduce a new one next to it. I'll probably close this pull request, open the other implementation as a draft, and try to slowly build towards it with the changes mentioned above split into smaller, incremental packages. |
| return *_gdtype_ptr; | ||
| } | ||
|
|
||
| bool Object::is_class(const String &p_class) const { |
There was a problem hiding this comment.
Wait, so, what is is_class exactly (i.e. what are we trying to optimize here)? The name seems... not great, to say the least, and the header doesn't have a doc.
Is it an instanceof operation? i.e. this->is_class("Node") === this instanceof Node?
Edit: I believe the answer is "yes", at which point this is a solid way to implement this operation.
There was a problem hiding this comment.
Yes, is_class is oddly named / designed. It already exists and should probably be revised at some point.
There was a problem hiding this comment.
In the theme of promoting the type-first architecture, maybe this PR should introduce Object::is_subtype_of(GDType const&) (optionally with an is_subtype_of(String) overload that resolves the type and calls the other one).
I realize right now we do a lot of "pass the StringName around and do lookups with it", but we ought to be in the mindset of "Pass the GDType around and do stuff with that directly"
| StringName name; | ||
| /// Contains all the class names in order: | ||
| /// `name` is the first element and `Object` is the last. | ||
| Vector<StringName> name_hierarchy; |
There was a problem hiding this comment.
Is there a point to have this at all rather than traversing the chain of superclasses? It seems like the issue is (potentially) memory locality, but seeing as StringName is interned "somewhere else" (meaning the accesses to evaluate == will need to reach that "else"), the space used by having a Vector on each GDType might not be worth it.
There was a problem hiding this comment.
I benchmarked a 3x speed improvement over the old is_class (hot access, cold should be even better). Having a Vector<StringName> for each GDType is not a big cost, so I'd argue it's worth it.
However, I've been second guessing adding this in the first "GDType" draft so I might revise / supersede the PR soon and propose this change later.
There was a problem hiding this comment.
The thing is the old is_class impl is so comically bad that we might get the same 3x uplift (or very very close to it) simply by traversing the links here. If you already have the benchmark setup, I'd be willing to bet that the pointer traversal is really close to this one without the extra vector.
FWIW the OpenJDK implementation for this problem does a pointer traversal through the supertypes, which makes me think that approach is very viable.
There was a problem hiding this comment.
The thing is the old is_class impl is so comically bad that we might get the same 3x uplift (or very very close to it) simply by traversing the links here.
Yea, that's definitely a possibility. It's a good reason to propose this change separately (if at all) from working towards first-class types.
I think there's value in both approaches there. Migrating to a new |
Re-assessing |
|
Incremental improvements tend to be the way to go. Though, on the merits, I think |
c5021b1 to
8bfb046
Compare
The type is currently bare-bones, but will be expanded in future PRs.
8bfb046 to
ac85d24
Compare
|
Discussed in the Core meeting. We want to go ahead with adding a new |
dsnopek
left a comment
There was a problem hiding this comment.
Thanks! This looks good to me, and should make a good base for further improvements :-)
Repiteo
left a comment
There was a problem hiding this comment.
While part of me still eventually hopes for an even more fundamental system utilizing type_traits, we've gotta start somewhere, and this is one hell of a way to start 👍
|
Thanks! |
Requires Use non-recursive mutex forStringName, improving performance #105192 (for refactor reasons).GDTypeto cover GDExtension types as well #106016This is a first step towards adding first-class types to Godot.
This is better explained in the proposal: godotengine/godot-proposals#12323.
After this PR, we can slowly build on top of it to move things from
ClassDBintoGDType.Implementation
I piggy-back off the previous
get_class_namevscaffolding, using the same mechanism to use and assignGDType *instead. For this reason, the implementation is safe and battle tested before being implemented.In particular, each
Objectowns aconst GDType *to its own type. The type exposes methods for getting type-related information.I make use of this new construct by optimizing
is_class. Currently, it is implemented as a virtual function, which usesGDCLASSto check against staticStringNamesingletons:godot/core/object/object.h
Lines 432 to 437 in e37c626
This is slow, not just because various checks were repeatedly called for each subclass, but also because the elements weren't local in memory, and each had to be put into cache separately.
Instead, I create a constant
LocalVectorof the hierarchy for each type, and simply loop through it to find the queried class name.(This would be much faster if the argument was a
StringNameinstead ofString, but that will happen sooner or later anyway as the code is refactored and optimized)Benchmark
I benchmarked a ~3x improvement of
is_class. The improvement would likely be better outside benchmarks because there are fewer cache misses.This printed:
7845mson master2662mson this PR