-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
/std:c++latest
makes headers much slower to include, up to 10 times
#3599
Comments
Thanks for the report and analysis. It's a difficult problem because C++ keeps adding features to existing headers. In some cases we can improve throughput by reducing header dependencies (fighting against the natural tendency of every header to want to include every other header), but it's a lot of work for often little reward. Ultimately this is a moot point in the world of Standard Library Modules. We may simply need to conclude, "Want throughput? Use modules." That said, refactorings to improve throughput are welcome as long as they aren't deeply invasive or widely source-breaking. (Every such refactoring has the potential to break source where users assumed X would always drag in Y; we can accept some amount of breakage in every release but it is a cost for us and users.) |
There's already a separated issue for Optimization of other headers seems easier to me. |
I totally understand, I'm the author of #355 :-), and I also for years trying to untangle Boost and make it include less, even though those changes are not always welcomed or sometimes simply undone few commits down the line. But on the other side: changing the standard headers parsing speed affects most of C++ users. Is it a small 'reward'? If you look at a single compile, but the downstream impact is substantial. Probably there are people who uses their huge build times as an excuse, but there are definitely people who cares about compile times and so much so that they do these:
From my experience: Boost.Spirit CI times are doubled on 14.3 compared to 14.0 (tripled for X3 which does not use precompiled headers). And this is considering that the main reason of huge Spirit V2 compile times is overzealous instantiation, frontend to backend emission, and backend inlining of that stuff.
I have tried to use modules. It was like VS doesn't want me to use them, passionately. First I just googled what modules standard library has and the standard itself seems to not mandate these names? Right away I found the tutorial https://learn.microsoft.com/en-us/cpp/cpp/tutorial-import-stl-named-module?view=msvc-170 which says: I continued. The tutorial talks about
Okay, googling more, I found the other list of names here https://learn.microsoft.com/en-us/cpp/cpp/modules-cpp?view=msvc-170#consume-c-standard-library-as-modules-experimental, it is
What, I should've installed them separately? Ehmm... Went to the installer, it warns me: Trying again:
Ugh, what else do I need? Finally found the
Btw, I'm not sure that the link http://go.microsoft.com/fwlink/?LinkID=691081 is pointing to the right location, looks like a landing page? |
I updated the table because I forgot about C headers. Now top 2 is |
It's a rough experience right now, sorry about that. While the compiler and library support is present in VS 2022 17.5, there are significant compiler bugs (tracked by #1694 where they affect the STL, and they should be significantly improved in 17.6), and the end-to-end story (including the build system, and IntelliSense) is incomplete (again, improvements scheduled for 17.6 and beyond).
C++23 provides the
That is technically true for all
You must build the shipped
This confusion is our fault. The Here's the proper Hello World for modules (until automatic build system support is available):
import std;
int main() {
std::cout << "Hello, modules";
std::printf(" world!\n");
}
|
So with modules the Hello World needs TWO very compiler specific command lines to build? Cannot
Even on warning flags? There is no way to selectively disable false-positive warnings form standard library in sources which encounter them and has to be globally disabled with modular std (because I guess it is UB to mix 'different' standard modules)? Sorry for abusing the thread. |
It's your own issue and this is tangentially related, so it's fine 😹 but you may want to join the STL Discord's channel if you want to discuss modules more. (There's also the Discussions tab in this repo, although I'll admit we don't check it frequently, whereas the Discord is highly active.)
Modules fundamentally change how we consume libraries, so the sequence of compiler incantations needs to change too.
Other implementations may do things differently, but in MSVC we're trying to avoid having the compiler become a build system. The compiler has enough to worry about already. So from a strictly compiler perspective, it needs to be told to build the Standard Library Modules before consuming them. Now, the build system (MSBuild or CMake/Ninja) will be able to automate this, since it will be informed by the toolset of the location of The good news is that building the entire Standard Library as a module is extremely fast (I measure it taking 3-5 seconds) and it can be reused until the compiler options change, or the toolset itself is upgraded.
Changing compiler options between module production and consumption is fraught with peril, since you're asking the compiler to persist its understanding of C++ library sources with one set of switches, and then reload it with a different set. We can't support that in general (e.g. changing Standard modes or anything truly invasive). Maybe you can get away with it for warning options, but maybe not - warnings can rely on a delicate dance between the compiler front-end and back-end. I would strongly discourage attempting to do this.
The ODR rules for modules are essentially the same as for classic headers. What's really bad, as I mentioned immediately above, is attempting to change options between generating If you built different flavors of For targeted warning suppressions, you can often pragma push-disable-pop around the instantiation of a library template. Sometimes you can vary the types you're giving to the library (e.g. to avoid sign/truncation warnings). And we provide extensive facilities to globally suppress warnings in library code only, without also suppressing them in user code; that could be applied when building and consuming the modules, just as with classic headers (this is one of the reasons why we need |
Thanks for the detailed answer, I'm really borrowing your time, but I cannot not comment on it.
Yeah... we now need a build system to even compile Hello World =/
The time shown again and again that such things are much easier solved by toolchain themselves and not by build systems. From top of my head: put standard library headers path to include search paths(!), linking the standard library(!), linking sanitizer libraries, linking atomic library, linking filesystem library, linking pthread,
It is for now, but two standard releases further it might get much worse :-)
I cannot imagine how I would know for sure what flags a third party library built the standard library with.
That probably another headache build systems need to solve because otherwise it will link two different std.obj and get a duplicates symbols linker error. |
I forgot that
|
Given the impact that /permissive- had on your results, is it possible that your measurements are more indicative of MSVC compiler performance than STL complexity? Running the script through clang-cl.exe as well might help isolate the STL itself as the culprit for slowdowns. And point to whether it's quality of implementation issues in the library or possible over-inclusion (whether mandated by the standard or not). |
/std:c++latest
makes headers much slower to include, up to 1139%/std:c++latest
makes headers much slower to include, up to 10 times
In my previous message I included both results with
More is included -> more has to be parsed -> slower compilation.
|
Have you benchmarked compilation times of full projects with different c++-versions? I mean, the numbers are really not great ( a full second compilation time, just because chrono is included is nuts) but would be interesting to see if/ when those differences actually matter for overall compilation times of projects and not just microbenchmarks. E.g. my observation with most projects I've been working on was that most compilation units directly or indirectly include a large part of the stl anyway (usually stuff like Filesystem is isolated to only a few compilation unit, but memory, chrono etc. is all over the place). So if a slowdown of a individual header is because it includes another STL-header, then this wouldn't matter to me, because I most likely include both headers anyway. On the other hand, there is probably little the maintainers can do w.r.t. slowdowns caused by new members that have been added to a particular header. |
From my understanding a lot of headers became heavy because of this chain
Yes, my times are doubled on 14.3 compared to 14.0, look here https://ci.appveyor.com/project/Kojoley/spirit/builds/46658394. |
|
Hmm, |
I don't think this is a major reason though... I think we can just use |
The tool takes inclusion graph and calculates for each edge how its removal would affect the compile time: Output for
Line 12 in 9231abe
Output for
It shows that removing |
FWIW libc++ has worked a lot on granularizing headers, and our C++23 CI job is 3 minutes faster than our C++20 job (C++23 is ~9min), mostly because we've removed a lot of transitive includes in C++23. |
@philnik777 IIUC it was done by splitting out headers like |
You can always run the tests against libc++ you have run here, right? If splitting up the headers is so bad on windows, it should be obvious by having much worse times than the STL. It's also quite possible that there is a sweet spot between what libc++ is doing and what you are doing here. |
It is hard to make a fair comparison. It seems that there is no easy way to test libc++ without building it, and even then it also seems to not work when clang targets msvc abi. Here are results from what is shipped with MSYS2 CLANG64 using its libc++ seems to be doing much better than MSSTL Same kind of a table like in the first post.
At least I learned that I need |
That's not a mild increase, but it's at least within the same order of magnitude. The C++ sodlib has grown a lot, especially with ranges and format, and it looks to me like splitting the headers up pays off.
We decided to remove all the transitive includes in C++23, since that breaks almost nobody right now, and people would simply have to add the respective headers as they migrate to C++23. But it feels to me like the 3min difference in our CI made it quite obvious that we should make the call and remove the transitive includes (even though that will break a lot of users). So yeah, I hope we remove the transitive includes soon. |
It looks like it does. Could be a direction for MSSTL, and opt-in/out macro too. @StephanTLavavej do you think #3631 could land with opt-in/out macro? |
@philnik777 though I am a little bit worried that clang might report wrong timing because by default it leaks memory and doesn't close resources, though sync on close might not be done by OS when the file haven't been modified. |
+60% for |
Given the metioned breakings mainly came from containers (#3631 (comment)), I guess we can move the inclusion of |
Times are reported by compiler frontend
/Bt
flag, >5ms >1% diff shown, min of 10 runs,/permissive-
was used on every/std
to exclude preprocessor/parser differences:<chrono>
<cmath>
<queue>
<stack>
<filesystem>
<numeric>
<utility>
<array>
<tuple>
<typeindex>
<algorithm>
<execution>
<functional>
<memory>
<stdexcept>
<bitset>
<string>
<system_error>
<regex>
<iterator>
<thread>
<list>
<sstream>
<random>
<valarray>
<streambuf>
<ostream>
<ios>
<future>
<iostream>
<deque>
<forward_list>
<mutex>
<locale>
<vector>
<strstream>
<fstream>
<shared_mutex>
<codecvt>
<condition_variable>
<iomanip>
<charconv>
<set>
<complex>
<scoped_allocator>
<unordered_map>
<istream>
<unordered_set>
<map>
<any>
<memory_resource>
<optional>
<string_view>
<variant>
<atomic>
<new>
<type_traits>
<exception>
<typeinfo>
<ratio>
<ranges>
<format>
<stop_token>
<syncstream>
<barrier>
Repro:
The text was updated successfully, but these errors were encountered: