Speed Up C++ Compilation
devtalk.blender.org
devtalk.blender.org
It allows them to enable the "unity build" stuff automatically for example, and on a per file basis as well. If you have a file checked out locally (in Perforce terms), it won't include it into the usual unity compilation unit for it and will compile it on its own as it's likely it's one you're going to edit a lot if it's one you've got checked out.
Anyway, I thought it was an interesting datapoint as I think it's quite unique. To the level of Boost using a modified fork of Jam at some point...
It has a nasty habit of getting itself stuck, and it's got a horrific architectural decision of only allowing one instance of the process to run at a time (and see previous point, it gets stuck). Combine those two with modern IDEs that call the build tool in the background and you bet a messy situation.
But the adaptive unity build is probably one of the coolest features I've ever seen in a build system
I figured this was possible but haven't seen anything that does it. I hate writing function signatures twice with subtle differences like default args and overrides. Waste of my time.
#include "MyClass.gen.h"
UCLASS()
class UMyClass : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere )
float MyFloat = 1.f;
UPROPERTY()
APlayerController* MyPc;
};
in a header, and you get garbage collection, serialisation, script binding, editor UI out of the generated code.Doing a full compile, has been known to bring even the most powerful machines to their knees.
They’ve employed a lot of the referenced article’s suggestions and it still is a massive code-base to compile.
[0]: https://wiki.documentfoundation.org/Development/BuildingOnLi...
cd $(mktemp -d)
fedpkg clone -a -b f38 libreoffice && cd libreoffice
fedpkg mockbuild
This requires the 'fedpkg' and 'mock' packages be installed, and that your user is in the 'mock' groupCompiling is one of many benchmarks I do, lol
Edit: note for posterity -- the branch will change as time goes by. Fedora 38 is the current release, so f38 is used.
The source for the code and the target for the build don't have to match. Look into different 'mock roots' at /etc/mock/*.cfg
You don’t need to do a full clone to have multiple working copies of your repo checked out. You can use the git worktree command to create these working copies:
For big repositories with long histories (like Qt), worktrees also save some disk space.
I’ll grant that saving disk space is a concern for many - not for me - the largest repo I use is 2-3 million LOC with about 10 or 15 years history and the 4-5GB per clone doesn’t bother me.
Right but that's quite a lot more faff than not having to do anything at all.
A task could be any small thing that requires its own checkout for some possibly stupid reason, like running the regression tester or temporarily running an old version of a binary out of your dev checkout over NFS if there was a bad deploy.
https://www.git-scm.com/docs/git-clone#Documentation/git-clo...
Sure, you can change back origin to point to the central one again, but you still have to do a dance to sync branches among your local clones (and I’m not sure what happens to the hardlinks).
worktrees just naturally basically are “views” of the same local repository, which may hang off a central repository (or not).
Also, all branches stay in sync between worktrees. For example, I do git fetch on one worktree, and remote branches are up to date on all of them.
The only downside is that they don't work with submodules, if you are unfortunate enough to be using submodules.
What we actually need is tooling to help recommend how to refactor or split headers in a way to reduce having to parse a ton of dead code.
Or even better, tooling that would generate one off headers per TU. I think it could be done.
Anyone have experience with PCHs? Good or bad?
Looks like https://include-what-you-use.org/ might do that.
One major deficiency in IWYU that we will have to solve is that IWYU only understands post-preprocessed sources.
If you have preprocessor guards for some code (as is done extensively in the Linux kernel) IWYU can recommend changes that break the build for other configurations than the one used when running IWYU.
stick everything in a single compilation unit automagically and have a flag to let bad legacy code that reuses global symbols produce errors/warnings so they are easy to fix, or be ignored so that we can still have backwards compatibility.
that is just the "hacky" way of doing things and it indeed works wonderfully, although often breaking any kind of incremental build - this is because we implement incremental build wrong, which is suitably easy to fix as well.
So then what does the goal become? Splitting the codebase into 8 equally-difficult-to-compile translation units? What about someone with 6 cores, or 12? Or 32?
I think devs working on large systems in soft-realtime domains are the ones to look at. There you'll often find build systems that are mostly unity builds, but that allow ad-hoc source files to be separately compiled, often with separate compilation options (such as with debug symbols and optimizations, etc.) that are too onerous to enable globally.
As far as problems with constructs like anonymous namespaces and name clashes are concerned, I think they're relatively easily resolved for new code, and these techniques are quite old. Many have been using this "new" approach for decades, so the "legacy" code is also clean in this regard.
But it's unfortunate that the net result is that anonymous namespaces are effectively useless. However, that's just case number 99 for "stuff added to the standard that you can't really use because of 5 or so other major problems with the language". And most new languages don't improve on any of this stuff, they just buy you off with other novel features -- admittedly sometimes quite intriguing ones.
Most "modern" languages are designed around building the whole world and statically linking it into your program. As you say, they don't improve on any of this stuff.
I would gladly use a new version of C++ that breaks compatibility but solve this problem.
This is the largest barrier for C++ right now.
It doesn't really have anything to do with compatibility (not entirely, but the things that are the biggest issue to good optimization quality and are fixable are things that need a system-level rethinking on how hardware exceptions happen). It just isn't reasonable to expect developers to know how to optimize, and it doesn't scale.
Well...
https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.htm...
And support for ccache is not an option to some build systems. For instance, Visual Studio projects in general and the msvc compiler require a lot of fighting to get it to work.
Also, ccache is renowned for not supporting precompiled headers.
Chromium eventually dropped support for such builds. At Google the developers have access to a compilation farm that compiles within like 5 minutes. With such farm unity builds makes things slower as they decrease parallelism. So Google decided not to support them not to deal with very occasional compilation breakage.
I lost count of the number of times that I had to deal with cyclic dependencies accidentally introduced by someone when they started to add #include without any criteria.
Their module support barely works, and will take a couple of years to catch up to VC++.
Other C++ compilers are even more behind in regards to catching up to ISO C++.
There's no good, fundamental reason why #include caching can't be done with similar mechanisms to import, resulting in similar perfornance.
Certainly, modules are cleaner than headers. That's a language improvement and a fine reason to switch to them. Headers leak more definitions, macros etc that are part of the implementation, and their interpretation is affected by definitions before the header is included.
But in normal situations those header semantic leaks need only add a very small or negligible time overhead to symbol resolution compared with modules, both from a compiler-friendly precompiled form. Articles I've see explaining why modules are faster tend to talk about parsing headers, repeatedly, and if not parse then still process a precompiled syntax in some complicated way, as opposed to modules storing a compiler-friendly data structure. But those are merely describing how things are already done, not what's possible.
So I would take a few minutes before wondering how bad they handled #includes.
Precompiled headers exist in the PC world, across all major compilers, since Windows 3.x days.
With the module, the giant list of names is compiled once into a large file, and then imported into consumers as necessary.
[1]: https://github.com/KhronosGroup/Vulkan-Headers/blob/main/inc...
[2]: https://github.com/KhronosGroup/Vulkan-Headers/blob/main/inc...
I'm wondering if anyone here remembers that article.
The way to do it is to never have any `#include` directives in headers. Put them all in the implementation source files.
You have a function in a `foo.h` which returns a `bar_t` type, that is defined in `bar.h`? Then each source (.cpp or .c) file that includes `foo.h` must first include `bar.h`.
While this does not completely alleviate the problem of reading the same header 10x in a project with 10 source files, it does mean that at least you don't read the same same 50x in a project with 10 source files.
Each header is read once, and only once, for each translation unit. When using header guards or #pragmas, a single header file will be read multiple times because it can (and usually will) include other headers.
https://en.cppreference.com/w/cpp/language/pimpl
Pimpl greatly reduce build times by removing includes from the interface header at the expense of requiring pointer dereferencing, and it's a tried and true technique.
Another old school technique which still has some impact in build times is to stash build artifacts in RAM drives.
https://news.ycombinator.com/item?id=24537267
https://www.cppstories.com/2018/01/pimpl/#pros-and-cons
It used to be a widely advertised technique maybe a decade ago, but it has long gone out of style.
If all you have to add is handwave over "Pimpl has so many other drawbacks" and to reply "google them" when asked to substantiate your baseless claim, I feel it was preferable if you sat this discussion out.
The noise you add is particularly silly as all your references point is the pointer dereferencing drawback I already pointed out and you claimed there were more.
For the rare cases where potential pimpl users care about heap allocations, there's a pimpl variant called fast pimpl with replaces the heap allocation with a opaque member variable that holds memory to initialize the impl object. Since C++11 the opaque object can be declared with std::aligned_storage.
No, not really. The pimpl idiom is a basic technique that is employed to "improve the software development lifecycle", which is a description straight from Microsoft.
https://learn.microsoft.com/en-us/cpp/cpp/pimpl-for-compile-...
Even Microsoft's Herb Sutter himself has quite a few articles on the virtues of pimpls and how to implement them.
If you think they are wrong, are you planning on reaching out to them to correct them and to try to educate them on the matter? That would be something.
This is a meaningless statement. As others in this discussion already stated repeatedly, pimpl is a very basic and fundamental C++ idiom that is pervasive in all application, and some frameworks are notorious for using them extensively even right now, as is the case of Qt.
At most, all you can claim is that you developed an irrational dislike of pimpls, but developers are renowned for making poor decisions and even introducing bugs, so it's worth what it's worth. I mean, some developers even in this day and age still complain about the whole notion of design patterns. Are these blends of opinions worth taking into consideration?
Because more often than not it is a made-up problem that at best is classified as premature optimization.
Take, for example, Qt and how it uses it's UI form classes. You can include it's autogenerated code by composition, inheritance, and through a pimpl. You are here complaining that everyone should care about heap allocations. Well, in this case they happen only at app start and behind a standard C++ smart pointer, and it's basically only used to instantiate a factory to create UI elements.
Can you tell me exactly any concrete reason why anyone should care about instantiating a few bytes in the heap with a smart pointer at app start?
Sometimes there are real problems,but sometimes there are made-up problems that have no relevance or meaning.
If you don't deal in those sorts of domains that's fine but don't dismiss them as those are some of the primary use cases for native languages. Pimpl is a hack, it's a clever hack in the context of C++ but it's not something intrinsic to native development. For instance it's a non-concern for languages like Rust(which just handle this whole domain much better due to not having to inherit a bunch of legacy behavior).
There's a bunch of feedback in adjacent threads that you seem to be ignoring so I suspect I won't change your mind here but I would encourage you to be a bit more open and consider that there are use cases where things like this matter rather than dismissing them offhand.
I think maybe you didn't read through both the items I linked or the many others that come from a simple google query. One of my links for example points out the memory fragmentation issues, which can also affect performance, as another commenter here has also pointed out. There's more to the story than pointer de-referencing or memory context -- many drawbacks worth knowing about.
There is nothing baseless here; there are pros and cons. But it's not in good form to ask people for details that are easily looked up on a topic as well-known as this one. We are not a reference source for you.
No, not really. That's only an option for the rare cases where you control the whole class and all it's members, and you can break any ABI whenever you like by converting any class to a protocol class.
In the real world, pimpls are applied to member variables that were encapsulated in the past but you want to remove their definition from the interface, or classes that are generated with code generators at compile time. It makes little sense to replace a class with a protocol+implementation+factory just because you want to get rid of a member variable or you need to consume a auto-generated class.
It just feels great to be able to totally refactor the implementation without touching the header.
It is not a language flaw. C++ requires types to be complete when defining them because it needs to have access to their internal structure and layout to be in a position to apply all the optimizations that C++ is renowned for. Knowing this, at most it's a design tradeoff, and one where C++ came out winning.
For the rare cases where these irrelevant details are relevant, C++ also offers workarounds. The pimpl family of techniques is one of them, and type erasure techniques are also useful, and protocol classes with final implementations are clearly another one. Nevertheless, the fact that these techniques are far from being widely used demonstrates that there is zero need to implement them at the core language level.
And private methods aren't exactly "rare cases". The situation is bad to the point that most good codebases make less use of classes, and many average code bases avoid adding private methods and resort to code duplication to a degree.
The reason why they have to be listed anyway could be 1) a vague idea of "consistency" with e.g. public methods and generally the enforcing access control only centrally from the class declaration 2) the idea of overriding the implementation in an inherited class. As far as I'm concerned, both are bad reasons to impose such an annoying limitation to the user of the language.
Or go the same route as namespaces -- mark start and end of the class implementation code (can be repeated) and nest functions inside.
There are other options if we ditch the C/C++ compilation model. Though arguably that isn't just bad -- it's an extremely simple way to achieve separate compilation without requiring a separate (probably binary, compiler-specific) representation for compiled interfaces. The latter could probably speed up incremental builds considerably, but it's possibly slower for clean rebuilds because of dependencies.
For me, knowing that I can change the implementation details of a class and it having no possible impact on whether other code compiles or how it behaves (assuming I maintain the same "public" behaviour) is absolutely a fundamental language feature. All I'm arguing for is that compilers should be able to make the same assumption - only the private implementation details have changed, so it's unnecessary to recompile other code that happens to include the header file defining some of those implementation details.
So what is a good justification for the current language design? I did find one SO post suggesting if your suggestion were possible, the overloading rules would probably have to change, but that doesn't seem like an insurmountable hurdle.
To be clear, what you're suggesting is that header file (foo.h/hxx/hpp) would have:
class Foo {
public:
Foo();
void doYourThing();
private:
int _privInt;
std::string _privStr;
}
then foo.cxx/cpp would have something like: /*private*/ Foo::ctorHelper() {
_privInt = 42;
}
Foo::Foo() { ctorHelper(); }
/*private*/ bool Foo::anotherHelper() {
return _privStr.find("bar") != std::string::npos;
}
void Foo::doYourThing() {
if (anotherHelper()) {
std::cout << "The foo thing\n";
}
}
Whether or not some sort of keyword is needed to mark the private functions as such is stylistic I suppose - I'd prefer it were there, but I'm used to C# where the access specifier is part of every member declaration anyway. But it's certainly not necessary - the compiler would just assume "private" if the declaration is not part of the class definition.
And yes, someone else could come along and put void Foo::anotherPrivateFunction() {
}
in their own code, but they'd never be able to call it anyway, so no harm done (arguably compiler could treat an "unreferenced" private function as an error in itself, but certainly the linker would just strip it out).Obviously one downside of the above is that if you wanted friend classes to be able to call such private methods, they'd either have to forward declare them, or you'd put them into a separate "foo_private" header file, but again, I'm not seeing why that's a big problem.
Irrelevant. Private member functions aren't mandatory or required, and when developers decide to use them they explicitly state the class needs to export their symbols.
Those developers who somehow feel strongly about private member functions are a multitude of techniques to meet their needs, such as using protocol classes and move private stuff to concrete implementations, or use non-member functions either with internal linkage or stashed in anonymous namespaces.
I don't see the point of complaining that something used wrong is not being used right, while purposely ignoring the myriad of options where things are right based on your arbitrary requirements. I mean, this aspect of C++ is around for at least three decades. Don't you think that if it was a problem someone would already have noticed it?
AFAIK there isn't a nice way to deal with this other than simply not using private members and coding in a simple C like style. I don't think you've shown a way, either. I don't know what you mean by "protocol classes", but if you mean abstract classes with virtual methods that need to be overridden, those are a bad solution because they overhead of vtables without any technical need or benefits (unless you want runtime polymorphism and vtables are exactly the kind you want).
This statement is incorrect. "Definition resolution" (my made up term for FE Stuff(TM) (not what I work on)) happens during the frontend compilation phase. Optimization is a backend phase, and we don't use source level info on type layout there. The FE does all that layout work and gives the BE an IR which uses explicit offsets.
C++ doesn't allow two phase lookup (at least originally); that's why definitions must precede uses.
Not really. There are two main flavours of pimpls: one where the impl class is a pod that only holds member variables, and one where the impl class holds both member variables and private member functions.
On both, the implementation can and does stay in the very same translation unit. On the former, the code stays in the very same class,without any change.
You only experience the sort problems you describe if you bring them upon yourself. That's hardly the idiom's fault.
I'm going to make it simple for you: take a Qt widget. You define its widget layout with Qt a designer, save a UI form file, and get Qt's UIC to generate a header file that implements that widget tree. You have three options to include the object in that header: either inherit from it, composition, and pimpl. So you swore off a pimpl. Pray tell, how do you use a Pure virtual interface in this case?
What leads you to believe they can’t?
> I'm going to make it simple for you: take a Qt widget. You define its widget layout with Qt a designer, save a UI form file, and get Qt's UIC to generate a header file that implements that widget tree. You have three options to include the object in that header: either inherit from it, composition, and pimpl. So you swore off a pimpl. Pray tell, how do you use a Pure virtual interface in this case?
Never worked with Qt. Can you give me FooBar example where pimpl is superior to pure abstract class, please?
As work, now, we mostly use C++11 and C++14, some projects are C++17, but I am not aware of a single C++20 project, I think all active projects that are C++03 or below have been migrated to something more recent.
Just a data point.
I was quite surprised to read this in their roadmap, as Unreal's codebase is quite massive and also uses some custom build tools.
2. Why do you dislike Java? Most of FAANGMULA uses it in some capacity no?
2. I was unfortunate to write some Java in the past. Strong dislike of it since. Nowadays I prefer natively compiled languages.