22 ms·
Eh, of all the things to criticize C++ over, compile times is somewhere near the bottom of the list. Compile times matter in extreme cases, but 3 seconds is no
by betterunix2 5y ago
Eh, of all the things to criticize C++ over, compile times is somewhere near the bottom of the list. Compile times matter in extreme cases, but 3 seconds is not really something I would worry about. I have seen Lisp compilers take longer than that just to start the REPL.
The fact that we are still dealing with weird problems with pointers, the fact that C++ has lambdas without garbage collection (explanation of the problem is too long for this comment), and the incoherent type system are much bigger issues than weird syntax or long compile times. The C++ feature set is a bunch of semi-compatible, sometimes outright incompatible (ahem destructors vs. exceptions and coroutines), ideas that keep getting extended further from compatibility by the standards committee.
- melony 5y agoWhat's this about lambdas and GC? Are they reference counted?
- jcelerier 5y agoNot at all. But if you are saving lambdas for callbacks or other async things you'll generally want to add your own layer of reference counting anyways, unless your software architecture is very clear from the beginning.
- tialaramex 5y agoLambda expressions are a place where it's particularly likely that your mental model of what's going on isn't correct, or fails to account for something important in some edge case and so you lose track of who "owns" objects and thus is responsible for cleaning them up and when they need to do so. With GC, this might cause a small unexpected leak of some kind. But in a language like C++ it can be Undefined Behaviour and all bets are off.
- brandmeyer 5y agoThat's part of why the capture list is explicit in C++. Automatic by-reference captures should only be used for lambdas whose scope is strictly lexical (ie, passed down the call stack, aka "downward funargs"). Otherwise stick with by-value captures.
- asveikau 5y agoC++ lambda captures can follow the copy constructor, so that when the lambda is copied, the captures copy too. Or you can capture by reference, which is easier to wander into unsafe situations with. So a lot of times if you want the captures to stay alive but don't want a deep copy, you'll make a std::shared_ptr<> and capture that, which leads to reference counted captures.
- jasone 5y agoI emphatically disagree -- compile times are definitely on my short-list of worst things about C++. Long compile times disrupt flow, and it requires great ongoing mental effort to work around slow compilation. Here's a poignant anecdote. At one point while working on HHVM at Facebook I finally snapped, spent 2-3 days doing nothing but optimizing the build system to speed up trivial incremental compilation (e.g. a whitespace change). My efforts resulted in... 24 seconds best case. I spent years on that project pipelining my coding, that is, making a small change, asynchronously launching a build, fixing issues from the previous build attempt, ad nauseam, and the pipelining was oftentimes 3+ deep due to latency. That cognitive load severely impacted productivity. That said, I agree that C++ has a lot of other terrible problems too!
- mpyne 5y ago> Long compile times disrupt flow, and it requires great ongoing mental effort to work around slow compilation. We once reimplemented automake in KDE land (into a tool 'unsermake' by Stephan Kulow), for a few reasons but foremost among them was that it reduced compile times, sometimes drastically.
- jcelerier 5y ago24 seconds is awful ? I have a 500kloc codebase which uses boost, Qt, and templates & modern features used very liberally and incremental compilation is ~1 second
- tom_ 5y agoIs it open source, and can you post a link? I have never seen this type of project even link in 1 second.
- jcelerier 5y agosure, it's https://ossia.io https://ossia.io. Here's a video of my edit-compile-run cycle: https://streamable.com/az397y https://streamable.com/az397y To get to something that fast for incremental builds of course requires some tweaks from the buildsystem defaults: - clang instead of gcc (I use clang on mac, windows, linux) - ninja instead of make - -gsplit-dwarf for debug info - mold instead of ld (I used lld before which was already nice but mold is bewildering) - PCH (very easy with cmake thankfully !) - split in shared libraries of adequate granularity, e.g. the software is split in ~40 plug-ins (although with mold I'm not sure this is even relevant anymore, the complete link step if I don't use shared libraries is not that slow). I've encoded most of these in my cmake toolchain-generator, cninja: https://github.com/jcelerier/cninja/ https://github.com/jcelerier/cninja/ To give some reference, my hardware is a 8c/16t intel 6900k. Edit: I did a complete build with everything statically linked instead of through shared libraries. To give a reference: lld (which is already fast compared to GNU ld and gold) links the entire software in 0.47 seconds ; mold links it in 0.3
- NL807 5y agoWhy do lambdas need garbage collection? What does GC offer that RAII can't solve?
- User23 5y agoI’m not sure about C++ in particular, but in general a lambda can capture a lexically scoped value and expects to still be able to access it even after the stack frame it got allocated in is popped. GC is the most straightforward way to keep that from leaking. On a tangential note, the amount of Greenspunning C++ has done over the last two decades is truly impressive. There sure is a lot of syntactic noise though.
- NL807 5y agoC++ lambdas are effectively callable objects, basically an anonymous class with an implicit operator(...) built in. They can capture other objects by value, which means those objects' lifetime is tied to the lambda object itself. When the lambda object goes out of scope, the captured objects' destructor will be called. There will be no leaks. Of course, if you capture by reference, this does not apply.
- betterunix2 5y ago"Capture by value" is meaningless since an object could itself contain references. Combined with the fact that lambdas can have side effects (there is no way to avoid this in C++) it is actually possible for a lambda to wind up owning itself. Imagine a class that has a pointer to a function object as a member, whose type is compatible with a lambda that captured an object of that class. Now that lambda might call a setter for that class member, using one of its own arguments as the argument to the setter. Apply the lambda to itself, and now the lambda owns itself via its ownership of the captured object. I have no idea what happens in this situation, but it is not at all impossible to wind up with something like this in a complicated and large codebase.
- nwallin 5y agoThat's why C++ requires you to explicitly list the stuff you want to capture, and whether you want to capture it by value or by reference, and the least-keystrokes way to capture is to capture by value. Capturing a reference to something on the stack is an option which requires extra work. C++ is predominantly a language that's oriented around value types instead of reference types. If you have a reference it's because you've taken extra steps to ensure the thing you have is a reference. As opposed to Java, Python, C#, Javascript, PHP etc where most everything is a pointer to somewhere. Suggesting that C++ adopt GC to solve the problem of capturing references on the stack in lambdas is akin to suggesting that the Netherlands solve its biking/transportation problems by subsidizing cars and gasoline. It's not even wrong.
- hsn915 5y agoThe whole point of using C++ in 2018 is to have control over memory layout and CPU instructions emitted. If you think lambdas with GC are a good idea then you would not use C++. You would maybe use Go. > Compile times matter in extreme cases, but 3 seconds is not really something I would worry about. I think a lot of people would be very happy if their projects compiled in mere 3 seconds.
- inetknght 5y ago> The whole point of using C++ in 2018 is to have control over memory layout and CPU instructions emitted. Memory layout, yes. CPU instructions emitted? Not so much.
- jcelerier 5y agoI really disagree, on some of my use cases I measured the difference between -O3 (sse2) and -O3 -march=native (avx2) to be 30-to-50-ish percent faster
- Quekid5 5y agoThat's as may be, but nowhere in your program does it say that it should use either sse2 or avx2, which was the parent poster's point. At least that's what I understood it to be. Technically you could have different code paths and inline asm, but that's not really specified by the C++ standard either.
- inetknght 5y agoI've found that `-march=native` with _any_ optimization level will almost always result in faster code. However, that faster code isn't always backwards-compatible to older generation hardware. And, where it is backwards-compatible, it can actually be slower.
- jhgb 5y ago> If you think lambdas with GC are a good idea then you would not use C++ Lambdas with GC are an extremely good idea, because of the silently shared environments and what not. Whether C++ should have lambdas if it can't guarantee GC is a completely different thing. C++ doesn't have to include everything to be fashionable, after all.
- political12345 5y agoyour comment makes no sense
- nicoburns 5y ago3 second would be fine; but I’ve heard of people with 45 minute compile times.
- lttlrck 5y agoI suffered 30 minutes until I installed ccache - now the build time depends on what has been changed but it's a tiny fraction most of the time - unless I hit a template, but even then it's still a massive improvement.
- isomel 5y agoCan't the build system already detect what changed? Is that not what ninja does?
- imron 5y agoI envy them. A clean compile on the codebase I'm working on takes over 2 hours.
- simplestats 5y agoDoes anyone using C++ for a real project actually enjoy 3-second compile times? It wasn't a great example to make their case (except in a narrow comparison to C), but it doesn't make for a very realistic counter-point much either. Compile times are definitely a headache for me.
- FpUser 5y agoI have decent size project and when I change something here and there it takes about that long to recompile and run. Full rebuild goes for longer but it is parallelized and is stile very reasonable.
- TaylorPhebillo 5y agoDo you mean as high as 3 seconds, or as low as 3 seconds? C++ compile times on a template heavy project I used recently were in the hours- you'd basically compile overnight and before you went to lunch.
- ncmncm 5y agoOnly if you are too masochistic to fix up your build.
- maccard 5y agoI'm sure projects like chromium and llvm would love for you to just fix their build times.
- tom_ 5y agoAnd Unreal Engine too, please.
- maccard 5y agoI used to work for Epic, and I did a good chunk of work on the game projects build times with reasonable success. Unfortunately I couldn't really change too much inside the engine because of backwards compatibility. Removing headers from other public interface headers has the possibility of breaking users code, which is a no-no so their hands are pretty tied. There are definitely some big wins to be had if they're willing to break back compat though!
- casion 5y ago> I have seen Lisp compilers take longer than that just to start the REPL. Except with Lisp, you aren't starting the REPL potentially every couple minutes. I've worked in many Lisps and it's not uncommon to keep a REPL open for days (recent project had one open for _weeks_) Back when I worked in C++, compile times drove me crazy. 3 seconds wasn't remotely normal even for tiny projects because there's the overhead of actually starting the compiling process, reading output, thinking about what you saw, repeat. Compile times in "the minutes" was much more normal.
- imron 5y ago> but 3 seconds is not really something I would worry about Except it's 3 seconds per file. The project I'm currently working on has 1.5 million lines of code spanning ~5,000 c++ source files. At 3 seconds per file it would take over 4 hours to do a clean compile. Luckily for this project it's not 3 seconds per file, and a full rebuild only takes about 2 hours - which is still a major pain. The concerns raised by the OP are completely valid, and cause issues for any medium-large c++ project. I'd love to have faster C++ compile times.
- ncmncm 5y agoAnybody complaining about C++ compile time but not using ccache, mold, or ninja has lost all griping rights. Building a 5000 file project using only a single core is beyond silly. Splitting the project into libraries that don't need to be rebuilt for normal development changes eliminates 90-99% of your build time. We can get into separate dwarf files after you get the basics down.
- usefulcat 5y ago> Splitting the project into libraries that don't need to be rebuilt for normal development changes eliminates 90-99% of your build time. ..for your particular use case* * your particular use case may not be representative
- Const-me 5y ago> I'd love to have faster C++ compile times. It's not an inherent property of the language. With some care, it is possible to write C++ code with compilation speed comparable to good old C, even in large projects. The worst offender is usually templates. Especially third-party libraries which use them heavily, like boost. Ideally, don't use these dependencies. Second best option, only include these libraries in *.cpp files which actually use them, and keep that number to minimum. When absolutely necessary, note C++ allows to split templates across h/cpp files; just because the standard library is header only doesn't mean non-standard templates need to follow the convention. Another typical reason is insufficient modularity of the code. It can cause some of the source files (especially higher level ones, like the one containing the program's main function) to include ~all headers in the projects. The fix is better API design between different components of the software. A good pattern for complicated data structures is pure abstract interfaces, this way the implementation stays private, the consuming code only needs to include the (presumably tiny) interface definition. Another good pattern is FP-style. Regardless on the style, I sometimes write components with thousands of lines of code split across dozens of source/header files, with the complete API of that component being a header with 1-2 pages of code and no dependencies. And of course you want all the help from the toolset you can get: precompiled headers, incremental builds, incremental linker, parallel compilation, etc. Most of these are disabled by default, but can be enabled in the build system and/or IDE.
- dralley 5y ago>3 seconds is not really something I would worry about 3 seconds to compile less than 100 lines of developer-written code?
- ajuc 5y agoI've had C++ job where regular build took 30 minutes and full rebuild over 1 hour. And it wasn't even a very big project. We moved the code to Java and the compile time there was under 10 minutes WITH tests (C++ version had none). Without tests it was less than a minute. It's not a perfectly fair comparison (we changed some things and not everything was moved to Java), but still.
- gpderetta 5y agoI have worked on C++ projects where the regular build was an overnight job. Fortunately these days I work on more saner projects which use distributed builds and a properly parallelized makefile and a full build is just a 2-3 minutes. Incremental builds are still slower than ideal though.
- torginus 5y agoIronically, compile times in the node ecosystem are much, much worse - and they are not even proper compilers, just bundlers/minifiers. Bundlers being written in JS with not great optimization, and the single-threaded nature of the whole thing means that people often sit for minutes while waiting for a change to compile. The only saving grace is that node projects on the side of millions of lines tend to be rare.
- eterevsky 5y agoIt's 3 seconds for something like 30 lines of code, which is completely crazy. Imagine if you project is 300'000 LoC.
- nottorp 5y ago> Compile times matter in extreme cases, but 3 seconds is not really something I would worry about. 3 seconds for 50 lines of code. How many seconds for 100k lines of code?
- einpoklum 5y ago> Compile times matter in extreme cases In an IDE, your program is partially compiled with every keystroke. So compilation time is a paramount concern, not in extreme cases but always, everyday, all the time. > 3 seconds is not really something I would worry about. That's 3 seconds for a single translation unit, and a short one at that with a single non-templated function. Now compile 1,000 translation units, each of which being, say, 3 times as complex (so, a decent-size project) - and your compilation time has become 144 minutes, nearly 2.5 hours - instead of 192 seconds, a little over 3 minutes. Now, it's true that you don't recompile your whole project every time, but still, the difference is huge. > The fact that we are still dealing with weird problems with pointers Actually, this has been turning into a non-problem in C++ with smart pointers and spans. More generally > C++ has lambdas without garbage collection I wrote this: https://stackoverflow.com/a/48046118/1593077 https://stackoverflow.com/a/48046118/1593077 a few years ago. You're welcome to link to an explanation of why you believe GC is necessary when using lambdas. > bunch of semi-compatible To some extent, certainly. But you need to account for two points: 1. C++ is multi-paradigmatic. You should not expect to use all features together. 2. While this may seem weird, or ridiculous, C++ is a work-in-progress language. There are issues which have been known for decades and are only now being addressed, or not even now. I mean, we've needed (some of) the ranges functionality since the STL was introduced in the early 1990s, and it has just now made it into the language. This may not be a good thing but it is _a_ thing, so it's actually not the case that the language > keep getting extended further from compatibility by the standards committee.