-
-
Notifications
You must be signed in to change notification settings - Fork 576
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
Builds are ~1-5mb larger than they used to be #1160
Comments
I made an attempt at trying to reduce the size of Funnily enough MSVC saw a 72% increase in binary size instead of the ~2% decrease I saw with Clang, which I can only assume has something to do with its link-time optimizer struggling to deduplicate the generated code that's now spread out over hundreds of object files instead. With that said, I'm curious about that third solution of yours. When you say "upgrade the wrapper", would that entail actually switching out the instance bindings on the engine side of things, or is that something that would be dealt with on the godot-cpp side of things? If it's the former, then the engine does technically have support for holding multiple instance bindings for one instance, since it needs to support multiple extension libraries anyway, so I guess you might be able to piggyback on that to also hold multiple instance bindings for a single library, and then also (somehow) sorting the ones further down the inheritance tree first, to ensure that they get found first in |
It would probably require some code on both sides. Basically, it'd change
I think we'd need to change how this works on the Godot side anyway. We'd want the wrapper that's the furthest down the class hierarchy to be the cached one (because it can always be upcasted to any of its ancestor classes). But we'd still need to keep track of all the wrappers that were created so we could clean them up once the object on the Godot side was cleaned up. Otherwise, we'd be leaking memory every time a new "upgraded" wrapper was created. Anyway, I don't know that this approach is even a good idea. :-) But it was one of the only solutions I could think of! Hopefully, ether options nr 1 or 4 will be acceptable, OR someone else will come up with a solution that's better than any of the ones I thought of |
Thank you for the breakdown. I guess it's a notable regression, but the size itself isn't all that outrageous, so I would personally be content with option 4. I am a little bit biased though, since I've dug myself a nice big hole by completely scrapping the default SCons/CMake setup in godot-cpp in favor of a custom CMake one, so option 1 would stir things up a bit, since I only ever build godot-cpp when my Git reference to it changes. |
37MB -> 86MB debug_draw_3d And now I can't use my patch to ignore unnecessary classes during build. But the main problem for now is that all methods that return I wonder what will happen if the tree of nodes is processed in the same way, in which nodes from other libraries or custom modules will be used or how it will work after updating the engine without updating the library?.. Because of this, GitHub CI spends 20+ minutes on a complete parallel rebuild, whereas it used to be ~5-7 minutes. My patch is here: |
Whoa, that's a huge increase! I wonder why you're seeing much bigger numbers than other folks?
This sounds like a bug in the code from PR #1050, but since it's not related to the size issue here, it should probably be tracked in a different issue. Can you create one? And, if you make a new issue, can you share which classes specifically it's having problems with and where they come from?
There is already code to avoid trying to find bindings for classes from other GDExtensions, and for core classes that aren't exposed. For custom modules you've added to Godot, you should probably update the extension_api.json used to compile godot-cpp. So, anyway, that's why I'm curious about which classes specifically are causing from for you - I need to figure out what the case we missed was :-) I hadn't really considered the case of updating the engine without updating the GDExtension. This should probably be handled the same as core classes that aren't exposed, but right now, I think it'll lead an error on the console and the Anyway, it's another issue we need to address! |
I'm assuming those numbers are non-stripped Linux/macOS binaries? Your distributed I don't have access to Linux/macOS right now, but when compiling your latest changes, on Windows with MSVC, your
It would be nice to see godot-cpp adopt something similar to the SCU builds that Godot itself recently introduced. I've been using CMake's "unity builds" to compile godot-cpp in Godot Jolt, and it cuts down on the godot-cpp compile times by some crazy number, like 90%, although don't quote me on that exact number. |
Android takes up the most space, although in fact on android this library should be a dummy at most 😅 current (+15MB because the 4.0
4.1
increase ~x2, without taking into account the addition of libraries for the editor.
But not all libraries are developed by yourself and some may not be updated for a long time. Then why do we need backward compatibility?
This is list of nodes that were not found if my patch was applied to the master branch:
Just do: cd godot-cpp
git apply --ignore-space-change --ignore-whitespace ../patches/godot_cpp_exclude_unused_classes.patch And build for
Linux and Mac stripped. Android is not stripped, because when publishing to Google Play, it is desirable to send debugging data. Google itself separates them and uses them in error reports. Using the standard strip from Ubuntu, it turned out to process only x86 libraries and the size decreased from 56 MB to 48 MB. I don't know if there is a strip specifically for android.
Jumbo/Unity builds? This patch is also in the patch folder.. It actually speeds up the build a lot and I use it locally on my PC, but for some reason the size of libraries on all platforms has increased quite a lot with it. |
Yes, I agree that this is a bug. Classes that aren't found should be treated the same way we are treating unexposed classes - it's just something that was missed. Let's fix it!
Oh, so you're using objects of these classes in your code, but you also want to exclude the wrappers from your GDExtension, such that they are just wrapped in That's also a case that I hadn't personally considered. Anyway, it would be fixed by the same change I described above (treating missing classes the same as unexposed classes). But that's still a different issue from the size increase. Even though they are both caused by the same PR, they will likely have different solutions. |
It would be nice without spam errors. I also created a separate issue where I added a little about the crash. |
Godot version
4.1-rc2
godot-cpp version
master (80986f8)
System information
Linux
Issue description
@Faless was the one to discover this with the webrtc-native GDExtension. However, you can see it even with the test project: with gcc on Linux, the debug template build is ~5mb larger than when using godot-cpp on 4.0.3-stable tag.
It's PR #1050 that led to this problem.
The reason is that before PR #1050, if you didn't use a class in your GDExtension, it wouldn't be included in the final binary - the linker just leaves it out automatically. However, an unintended consequence of that PR is that now all classes are always included in the final binary.
That PR aimed to solve a bug where the godot-cpp wrapper class that was created for a Godot object was (before the PR) the class of the argument or return value. So, if you called
get_node("MyNode")
it would always wrap the object in theNode
wrapper class, even if the node was actually aSprite2D
. And because Godot caches the wrapper class, it would forever be stuck as aNode
wrapper. If you had another method that passed in the same object as aSprite2D
, it would unsafely cast the wrapper toSprite2D
even if it wasn't actually one. Despite being unsafe, this actually works in a surprising number of situations! However, there are case where it causes problems, like with virtual methods. (And it's also plain incorrect C++ :-))Anyway, that PR solves the problem by making a big
HashMap
of the binding callbacks for all the Godot engine classes, so when an object is sent over to godot-cpp, it can use the correct wrapper class (ie. the one matching its class on the Godot side). It's thisHashMap
that's causing all classes to be "used".I've thought up 4-ish possible solutions (although, there's probably more - please share your ideas!):
HashMap
that were discovered in the scan (and all their parent classes). I don't think this would be the hackiest thing in the world? Header file includes are pretty easy to scan for. And it means that GDExtensions will work as expected out-of-the-box, but you can optionally take this extra step (which we could include in an official godot-cpp template) that optimizes the size of the extension. However, I know that not all people want to use scons, and they'd need to do their own optimization step.Object::cast<T>()
would also create a new wrapper when doing a valid downcast. However, this would likely be slower, since we'd be creating and throwing away way more wrappers. And, actually, I'm not sure how those extra wrappers would get cleaned up?Object::cast_to<T>()
to "upgrade" the wrapper to one that's further down the inheritance tree, and overwrite the one in the cache, if it's a valid downcast. This would also be somewhat slower (and maybe surprising, because you'll get a different object than you put in) and also leaves the open question of who cleans up the old wrappers.What do you think?
Steps to reproduce
Minimal reproduction project
n/a
The text was updated successfully, but these errors were encountered: