-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
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
Consideration of using unity build system for Godot #13096
Comments
Not to be confused with the game engine of the same name, to which it seems to be completely unrelated. |
It would have to still involve SCons due to the amount of logic required for support of multiple platforms with one build system. I don't think it is impossible, just that it would take a lot of work. All of the SCons files would need work. |
To quote the docs: And it seems Unity builds (or really: one big header file including everything) would only be useful as an addition, not a replacement. But still: Is this really a pain point for people? Honest question, for me the engine compiles quite fast (or at least, it never bothered me). |
I'm trying out some things for this and the build time is reduced by about 30%, Here are some tests. The environment variable For unity builds to work there are some things that need to be refactored first: Many third party libraries and modules don't have include guards like this:
So, I needed to have a blacklist with the sources that won't added to the master files. There are declarations of the same static functions in multiple places, like this one:
For those I needed to move them to a single header with include guards, like In 2.1 the same
So it needs a I think those are minor changes, and the thing about include guards, and avoiding redefinition of static functions everywhere is probably a good thing to have anyway. Also, since SCons doesn't support unity builds the scripts need to be changed to store all the sources for each target in lists instead of calling directly drives/master.gen.cpp
drives/master_2.gen.cpp |
Late to the party here, but I had a little go yesterday afternoon at converting some of the latest master to use unity build / single compilation unit techniques. I just did a little low hanging fruit to assess how difficult it would be, what kind of differences we could expect. My method was a little different to rraallvv's suggestion as I only found this thread after investigating this myself independently. MethodFirst of all I wrote a batch file to put all the cpp files in a folder into a list of #includes for a single compilation unit. My linux batch-fu isn't very good and the ignore seems broken but it was something as simple as this:
And there is a recursive version which searches subfolders too:
However in practice I mostly used the former. Most of the SCsub files for scons contain a line such as this:
Adding all the cpp files in the folder to the build. So in order to get a unity build, essentially all we have to do is run the batch file to generate a single compilation file (I've named SCU.cpp but it could be anything). E.g.
and then change the line in the SCsub file from *.cpp to SCU.cpp. There is I'm sure a way of changing the source files in the SCsub automatically according to a user switch - I know next to nothing about scons but just note that it is possible very easily to have both a normal build and unity build available. ChangesDepending on how 'strict' the codebase is, particularly concerning the global namespace determines how easy it is to get things to work. Here's a couple of links which mention some of the issues: http://onqtam.com/programming/2018-07-07-unity-builds/ Essentially some ye olde and hacky techniques which pollute the global namespace can lead to conflicts. Luckily these were rare in most of the sections of Godot I got working. The most common problem was use of a static 'global' function within the cpp to localize the use to the particular file. This pollutes the global namespace but you can normally get away with it because it only pollutes the compilation unit.. except when you start using unity builds. 2 easy example ways to fix this:
As an aside this also showed up a code smell .. in some files particularly in the visual script module certain static functions were repeated copy and paste in several files. It would seem to make more sense in this case to have one version of the function and reuse it. However this is just a side point, not necessary for the purposes of the unity build. Another issue that can occur is with #defines. You occasionally get more than 1 cpp in a SCU file trying to define the same symbols. The compiler will complain and it is normally pretty easy to figure out. This is something that care should be taken over though because of the potential for subtle bugs, particularly if you maintain both a normal and unity build. A solution could be something as simple as systematically #undef the symbols at the bottom of cpp files that define them. ResultsSome of the folders I managed to convert fine so far:
and some other misc folders like bullet. Full rebuild timesNormal: 6 mins 35 Touching engine.h (included in a lot of places)Normal: 2 mins 57 Incremental build times (touching one cpp file)Normal: 0 mins 22 Binary size (godot.x11.tools.64)Normal: 278 megs DiscussionThe conversion was actually rather simple. I spent the largest amount of time googling how to write a batch file. Fixing any compilation errors was not very involved, about half the folders that I tried worked straight away with no changes. From only changing maybe a third(?) of the folders we already have a halving of full build times. At a rough guesstimate I think we could probably get a full rebuild down to something like a minute with nearly everything done as unity build. Full rebuilds are not quite as important as the other two incremental timings in terms of developer iteration, but are certainly very important in terms of compiling different branches, and the travis etc builds (should they be done with unity build), and 6x quicker is not a bad improvement. Amazingly even touching a single cpp file incremental build seemed to go quicker with the unity build. Sometimes this can be slightly slower when converting a unity build. This may have been faster because the linking stage was easier. Finally the binary size was smaller. I haven't thoroughly tested this yet, and this may be purely debugging symbols, but any decrease we can get in the binary size for the engine would be welcome, especially on e.g. mobile. Speedwise, using a unity build means you get link time optimization essentially for free, afaik. So I would expect the binary to be, if anything, faster at runtime than a normal build. Some downsides
In my experience 3 and 4 are the biggest considerations. There is also the issue of if you incorporate unity build techniques, whether to maintain a 'normal' build and a unity build. If you do decide to offer both you have the problem of ensuring PRs do not break either build. This is doable, and some shops do this, however my personal preference is to only maintain a unity build where possible, as it is far simpler. Should we start incorporating unity build techniques?In my experience this can be quite a revolutionary change. There are some downsides, but if you can e.g. half the iteration time, you can theoretically double the productivity of each developer. A developer sitting around waiting for a compilation to complete is a wasted developer. Not only that, they are likely to lose their train of thought and go off and do something else if changing a header file results in a 3 minute recompilation. There may well be naysayers who have not used unity builds before. I would suggest therefore that it might be an idea to have a gradual introduction of these techniques, to test the water. An example is the bullet source code included in third party. They actually INCLUDE a unity build for bullet in the source code, but we don't use it. If you look in thirdparty/bullet you will see:
These are, tada, single compilation unit files. So the first thing we should probably do is be using these. I think there is also a good argument for creating SCU builds for many / most of the third party source code to get us started. This will give us some increase in full rebuild speeds with very few downsides. Anyway these are just my thoughts on the subject, for further discussion. If anyone wants to try this for themselves, they can download and compile my test fork from yesterday's master: |
@lawnjelly since the build cache was improved builds are considerable faster now and work for collaborative environments too. Unity builds might have a detrimental effect for incremental builds and collaborative environments, but I still think those would be a nice addition for solo developers and could bring other benefits like smaller binary files. Unfortunately I don't see that happening anytime soon. |
I've only been compiling the source for a few short weeks, and I'm sure improvements have been made in the past (scons seems very convenient and adaptable), but surely that shouldn't be an argument against making the build faster still, if this can be done relatively painlessly? And the collaborative builds sound interesting, are there any docs to explain how to do these (am I missing out, is everyone else using these 😳 )?
I suspect we would need empirical testing for this (like most optimization), I'm always finding the results can be quite counter-intuitive. For instance the tests I've done so far with incremental building are actually faster with unity build (the linking becomes a greater proportion of the time taken (link optimization is also an exciting area)). Maybe try the fork I linked and we can see if it is faster for you? And I certainly don't want to come across all 'we must convert everything to use unity builds', I'm not trying to push that - there are pros and cons in different situations .. the potential pitfalls of symbol conflicts might be too much of a risk in some areas, we don't want to create bugs. It is interesting to try different approaches though and it can be fun researching this kind of thing. 😄 If nothing else, it might be beneficial to build some of the third party libraries in this way (if they are compatible). For myself I found that the build times could be a bottleneck, particularly when working on things like android builds, where I had to build repeatedly for different chipsets according to the test devices. Certainly no one goes to their grave wishing they'd spent longer in front of their PC waiting for it to compile. 😁 |
@lawnjelly I haven't been active in the community lately, but if I remember correctly there are a few environment variables that have to be set in order to enable the build cache and builds from different participants in a collaborative environment have to be queued so that two builds don't modify the cache at the same time, you might want to take a look at some of the scripts in this repository https://github.com/GodotBuilder/godot-builds/tree/master/scripts |
I've done a bit more work on this in an experimental build. I've now got the main first party Godot code compiling very quickly, however aside from bullet and assimp, quite a bit of the thirdparty code is messy c, and has a lot of static global namespace violations, so I have mostly given up on converting the rest of third party. ResultsAs a result the full rebuild time is mainly bottlenecked by thirdparty, however I've had continual improvements in the other incremental builds (timings are mins : seconds) : Full Rebuild Touch engine.h Touch engine.cpp File sizes (x11, tools, debug_release, fully stripped) So roughly speaking if you are developing on a compile / change / test / compile cycle you might expect a 4-6x speedup, and full builds (for example for different platforms) should be more than 2x speed. AimsBasically my aims have been to allow unity builds (especially for devs), but to have effectively zero impact on everything else. At the moment I have added a switch 'unity' to the Scons parameters that can be set to True. For everything to do with the unity build, I have currently isolated it into one directory: In the affected SCsub files there is a simple if .. else:
In fact I'll try and move all the logic in the unity section out into the misc/scu folder, using an 'include' or whatever the python-esque equivalent is, that way the SCsub files should never need to be touched after initial setup. In the misc/scu directory you start with one file, SCU_Build.py. This will automatically create (in the blink of an eye) unity build files for every SCsub file we are interested in. At the moment I just run this manually, but it could be added to the main build process if desired. The SCsub files add the relevant SCU_~~~~.cc source file instead of the .cpps, and with luck, it all just works (TM). Some problems I had to overcomeBuild orderOne nasty problem I had to work around yesterday is that the unity builds exposes a bug whereby sometimes the cpp files are compiled by Scons BEFORE the generated files (gen.h) have been created. This is either similar or the same issue as #5042 . I figured out that this was caused by Scons not having the proper dependency information, and therefore the build order being incorrect. So the solution (at least in my case) was to manually specify the dependencies for the unity files, such that the gen.h files were generated first. E.g.:
This tells Scons that it must generate app_icon, splash, and splash_editor BEFORE it tries to compile SCU_main.cc. Excluding files from the unity buildOn occasions, particular files or folders caused problems with the unity build. The simple solution was to exclude them. Inside the SCU_Build.py auto-generator it was easy to put files into an 'ignore list', and then they could be added manually after the unity build file to Scons. Here is an excerpt from SCU_Build.py:
The arguments are the source folder, the file type we are interested in, and the output file name. With process_ignore the extra argument is a file to ignore from the list. core/make_binders.pyThis file seemed to be doing some 'magic', and one of the generated files needed a #pragma once because of a multiple inclusion problem, but only on the unity build. I need to investigate this a little more for the best solution. IntegrationI brought up the unity build in #godotengine-devel on irc the other day to discuss, and mostly the responses were positive, in particular Akien was open to the idea. My view is that if it can all be added painlessly in (almost entirely) a separate folder from everything else, can be totally switched off, and does not complicate / affect the current build, it is well worth trying, considering the benefits. As I say my aims are to produce something which is totally reversible, with next to zero impact on everything else in the build, which can be simply activated by developers by adding a 'unity' flag to the scons parameters. If we decide in practice it is more hassle than it is worth, we can simply remove the folder from git and revert the small SCsub differences. The advantage of maintaining everything to do with the unity build in one folder, is that if any changes in git that are needed to maintain the unity build (which will happen occasionally, for instance changes to generated files) they can be done in one place isolated from the rest of Godot, and the policy on changing these files can potentially be a lot more lax than with standard PRs, as they don't affect the normal build. I'm a beginner with git, but if we can get the main build compiling completely fine with the unity folder as optional, it could even potentially be maintained off the main git repository? So once I have a good solution for the couple remaining issues I will remake the changes as a potential PR. So please let me know any preferences for naming (scu versus unity) and location for the folder. Unofficial versionIt has also occurred to me that initially for a test I can make a totally separate system, with a different repository, with the unity folder, and add a python script which makes the necessary modifications to your SCsub files automatically. This will allow any of us devs to try it out (as long as you remember not to check in your modified SCsub files to git afterwards in a PR lol! 😄 ). In fact thinking about it, maybe we would be better (at least for now) implementing this as a separate repository that links to the main build. Watch this space! 👍 |
As for naming preferences Google calls it jumbo builds https://chromium.googlesource.com/chromium/src/+/lkgr/docs/jumbo.md |
OK, after a day slaving around writing a patching system, my unofficial version seems touch wood to be working, and is ready for testing by anyone foolhardy enough: Note that it is only so far aligned to the current master (or a recent-ish version). If it proves useful we can easily make modifications for the other forks. The install instructions are in the repository. In short:
ImportantThe script patches (modifies) many of the build files. As such it is essential you try it (at least until it is stable, and you are familiar with how it works) on a temporary version of the godot source and not on your current build. You have been warned! 😄 Incidentally, if you want to compare compilation timings between normal / unity builds, for a rebuild all, the following might kind of work:
I've been meaning to figure out how to get Scons to delete the object files as part of the clean. It probably needs an SCsub file in godot_SCU, something like that. |
On the suggestion of @bdbaddog I changed the includes in the unity build files from using macros to being straight paths, as it seems scons can't deal with the macros. This is the reason why I was having to put in explicit dependencies to the generated files (gen.h). Old:
New:
While it did have the desired effect of no longer needing the explicit env.Depends call, it had an unexpected side effect of slowing the build from 6 seconds to 28 seconds for a touched .cpp file 😮 ! For this reason I have reverted to the macro approach. Perhaps scons parsing the source for dependencies is actually a bottleneck in such scenarios? Maybe it is only an issue in unity builds. It will be interesting to do some profiling on this versus the normal build if I have time. |
That's to be expected. It's entirely possible this is only with Unity builds if the header files include lots of other header files. |
Arg you are absolutely right. This is wiping out a lot of the lead on the incremental builds (and indeed the touch of a single cpp is slower). Figures now are: Full Rebuild Normal 6:35 Unity 2:40 This makes things a lot less clearcut, considering the pitfalls. 🙁 I'll continue to do a bit more work on this and see if I can increase the margin. Maybe some more traditional other techniques would be needed to further decrease the build times (forward declaration, more modular linking etc), however that kind of thing is harder to retrofit. |
@lawnjelly - What version of SCons are you currently running? |
2.4.1 it says using scons -version. Probably far out of date, good point, if I'm trying to time these things. I'm presuming it's just the one provided on my linux mint, I'll try out the most recent version tomorrow. 👍 |
@lawnjelly - That's ancient.
Try setting up a virtualenv and install "pip install scons". 3.1.1 is the newest. There have been many improvements since 2.4.1 |
A simple hack for this that I use is |
Or |
Note: Before opening a proposal, see the discussion above for possible technical difficulties. Feature and improvement proposals for the Godot Engine are now being discussed and reviewed in a dedicated Godot Improvement Proposals (GIP) (godotengine/godot-proposals) issue tracker. The GIP tracker has a detailed issue template designed so that proposals include all the relevant information to start a productive discussion and help the community assess the validity of the proposal for the engine. The main (godotengine/godot) tracker is now solely dedicated to bug reports and Pull Requests, enabling contributors to have a better focus on bug fixing work. Therefore, we are now closing all older feature proposals on the main issue tracker. If you are interested in this feature proposal, please open a new proposal on the GIP tracker following the given issue template (after checking that it doesn't exist already). Be sure to reference this closed issue if it includes any relevant discussion (which you are also encouraged to summarize in the new proposal). Thanks in advance! |
Consideration of using unity build system for compiling Godot engine. The unity build system compiles all source code as a single translation unity and therefore might decrease compile times significantly. More info : http://buffered.io/posts/the-magic-of-unity-builds/
Also if you want modularity you can compile chunks of translation units like one for sound ,UI , render, editor .. etc.
The main focus of doing this is to reduce compile times and increase productivity of developers.
The text was updated successfully, but these errors were encountered: