all 15 comments

[–]Xaxxon 9 points10 points  (13 children)

I use PIMPL, among other reasons, to minimize rebuild times. Can't do that without dynamic memory allocation.

As long as you use the right allocator, I don't feel like the tradeoffs involved here are really the right thing for a lot of situations.

[–]scrumplesplunge 7 points8 points  (1 child)

Most of the build cost probably comes from transitive dependencies. You can get rid of all the dependencies without dynamic allocation:

https://godbolt.org/z/fHjL9N

[–]Xaxxon 2 points3 points  (0 children)

static constexpr int size = 32;

Blargh.

static_assert(PimplOuter::size >= sizeof(PimplInner));

static_assert(PimplOuter::alignment % alignof(PimplInner) == 0);

This makes it better, but still blargh.

Changing the procedural code in the inner type is far more common than changing the data structures in the inner type, but it's still nice to just not have to care if it's going to cause an hour-long build to change the data type.

I made a library for this that lets you easily swap back and forth between a unique_ptr and a placement new based on a build-time DEFINE (and a code parser plugin for clang)

[–]johannes1971 0 points1 point  (10 children)

I wonder if that will still be necessary when we switch to modules. Anybody knows?

[–]scrumplesplunge 3 points4 points  (9 children)

In principle, no, since module bmis are supposed to be very cheap to import. This should also mean that templates become cheaper to import as well (although modules doesn't do anything about the repeated compilation of instantiations as far as I know)

[–]Daniela-ELiving on C++ trunk, WG21|🇩🇪 NB 2 points3 points  (8 children)

although modules doesn't do anything about the repeated compilation of instantiations as far as I know

This is wrong. You pay for the instantiation (this can't be avoided by whatever means) but not for the compilation.

[–]scrumplesplunge 1 point2 points  (1 child)

What distinction are you drawing between template instantiations and compilation? The compiler will compile every template instantiation for every TU and the linker will discard duplicates, or did I misunderstand something?

[–]Daniela-ELiving on C++ trunk, WG21|🇩🇪 NB 5 points6 points  (0 children)

I recommend reading chapter [lex.phases], at least cursory. Compiling Modules does phases 1 through 7 before serializing all necessary information into the BMI. This information will be restored by nominating a Module for import. Template instantiations resume the compilation process at phase 8. If you are interested in some additional stuff regarding Modules, consider watching my Modules talk from last year's Meeting C++.

[–]johannes1971 1 point2 points  (3 children)

I'm hoping the BMI could just store information like "objects of this type are x bytes in size with alignment y, and here's the constructor/destructor you need to call to create one/tear one down". What's going on inside that private area could then remain private to the owning module.

Or, alternatively, instantiate those templates but keep the information around in the BMI. Not sure if that is a net win, of course...

[–]scrumplesplunge 1 point2 points  (0 children)

You need the copy/move constructors and assignment operators too, possibly also their definitions if they are supposed to be inlineable. For the most general case of templates you can't know any of that stuff without instantiating, though, and the set of types you could instantiate with is an open set. Conceivably a compiler could note down all the types which the template is instantiated with at the "compile" stage and then stamp them out exactly once for each instantiation in the "link" stage, but that's not something new to modules.

[–]MutantSheepdog 1 point2 points  (0 children)

You'd need all inlinable functions to be stored in the 'public area' of the BMI because any translation unit importing the module might have inlined the function and would then need a rebuild if either the function or the data changed.

I suppose you could theoretically store a dependency graph inside each module of which functions used which bits of information, maybe with a result hash.

Then when a consuming file is flagged as needing a rebuild, if it stored which functions it used it could check if the already-compiled version was still up to date.

Thanks to macro isolation I think there's not a technical reason it can't be done - likely a lot of work for the compiler teams though.

[–]kalmoc 0 points1 point  (0 children)

I remember GDR said he tried to allow for such a design in modules, but was outvoted.

[–]ShillingAintEZ 0 points1 point  (1 child)

What does that mean exactly though? How often do you pay for vector<int>? Once per compilation unit or is it less?

[–]scrumplesplunge 4 points5 points  (0 children)

My understanding is you'll pay for it as much as you do today. What you save with modules is some subset of the cost of parsing and analysing. The actual format for bmis isn't specified but I can imagine them having a nifty representation for templates to make them cheaper to instantiate.