4 ms·
> For example, the ABI used by Linux already passes structs in registers It's a bit more nuanced I think - what I understood from that talk is that the "overhe
by menaerus 3y ago
> For example, the ABI used by Linux already passes structs in registers
It's a bit more nuanced I think - what I understood from that talk is that the "overhead" of a unique_ptr is attached to the inability for a compiler to pass the unique_ptr as a value through a register but will instead have to use the stack (memory).
That said, this nuance of using stack instead of registers doesn't apply in the general case but _only_ for the unique_ptrs holding an object with non-trivial copy-constructors or non-trivial destructors - so, non-PODs.
If we accept this nuance as a measurable cost in actual workloads where it really matters, this is again only the artifact of Itanium ABI and something C++ can only adhere to but cannot change. Any language that speaks to the system (OS) must do the same.
That said, among many other and much more complex code transformations that we do I can hardly see using push/pop instructions as a bottleneck - our CPUs indeed implement push/pop (micro)instructions through a dedicated HW circuitry meaning that they're always on the fastpath and we don't pay the price for them as we do for "regular" instructions.
This is why I call this talk a BS and I see it only as a way how Google wanted to prove that "break the world" is worthy under the disguise of zero-cost abstractions that are only "sometimes" zero because of "C++/ABI".
- AshamedCaptain 3y ago> It's a bit more nuanced I think Yes, I have edited it a bit; I did not want to claim that unique_ptr is universally passed through the stack, just that it is not an insurmontable problem to do so. > this is again only the artifact of Itanium ABI and something C++ can only adhere to but cannot change. Any language that speaks to the system (OS) must do the same. This is not true, though. 1) you can change and evolve this ABI (it has happened in the past), 2) you can also have an ABI for the system libraries and an ABI for the rest (see Windows, or see how any language other than C works), and 3) since the problem would only be about non-PODs, which are pretty much C++ specific, the system ABI would not be affected at all.
- menaerus 3y agoI see, thanks for clarifying, especially on number (2) which I wasn't aware of. I always had an impression that the compiler ABI is somewhat tightly coupled with the system ABI and which is why I used them almost interchangeably. So, Google premise is that they can provide alternative unique_ptr implementation in a way so that it breaks the C++ ABI without touching the Itanium ABI but with the benefit of unique_ptr fitting into the registers always rather than almost always? If so, I am not aware that they have shown any such implementation and its impact to their codebase?
- AshamedCaptain 3y agoYou won't be compatible with the SysV/Itanium ABI (it does include C++ bits), but you can definitely do it. It just happens that you will probably not have any problem to call a C library from such a new ABI (incl. libc and the kernel) since you only changed non-POD behavior, and those C libraries won't use anything but PODs. The fact that you can have some structs with non-trivial methods that are still classified as PODs means the ABI is already solving the hard problem (i.e. these methods won't really have a 'this' pointer), so the limitation for types like unique_ptr sounds like easy to remove in an experiment.
- menaerus 3y agoSo ... it appears that LLVM actually did the necessary code changes - libc++ with [[clang::trivial_abi]] on std::unique_ptr guarantees that the std::unique_ptr is always going to be passed directly through a register: https://libcxx.llvm.org/DesignDocs/UniquePtrTrivialAbi.html https://libcxx.llvm.org/DesignDocs/UniquePtrTrivialAbi.html Caveats outlined by the document: > Function definitions now require complete type T for parameters with type std::unique_ptr<T>. The following code will no longer compile. > Lifetime of std::unique_ptr<> arguments end earlier (at the end of the callee’s body, rather than at the end of the full expression containing the call). > Lifetime of local returned std::unique_ptr<> ends earlier. So, switch to a new ABI alone requires (1) a lot of manual code plumbing, e.g. replacing all your std::unique_ptr with forward-declared types with corresponding header includes across the whole codebase and (2) more serious session of discovering and fixing new std::unique_ptr runtime behavior using ASAN. Benefits of this ABI change measured on the Google-scale? This is what they have to say: > Google has measured performance improvements of up to 1.6% on some large server macrobenchmarks, and a small reduction in binary sizes. IMO pretty serious caveats for no obvious benefits, even at such a large scale as Google. Measuring 1.6% of runtime performance benefit on macro-scale is hard, if not impossible, to differentiate from measuring the noise.