5 ms·
C++ is fast in theory, but if they can't get C++ compiler (LLVM) to squeeze any juice from CPU, then what's the value of that language from a software engineeri
by Ygg2 2y ago
C++ is fast in theory, but if they can't get C++ compiler (LLVM) to squeeze any juice from CPU, then what's the value of that language from a software engineering viewpoint.
Hopefully, you can see why this reasoning is a problem. The main stumbling point being compilation speed != runtime speed.
- amelius 2y agoI have been using make's -j flag to compile C++ projects with great success. The main point being, Rust focuses on the wrong type of optimizations.
- deciduously 2y agoWhich is exactly what this project is now able to do. Your parallel make jobs don't help if you have one gigantic compilation unit, as they originally did.
- wahern 2y agoLTO can be parallelized, both implicitly from the Makefile, and also within the compiler. GCC's -flto itself takes an optional argument to control the number of parallel threads/jobs. See also the -flto-partition option for selecting symbol partitioning strategies. Rust/Cargo does this automagically, except the only control you have are the crate and module boundaries. The analogous approach for C is to (optionally) manually group compilation units into a smaller set of object files in your Makefile, LTO'ing each object file in parallel (make -j), and then (optionally) telling the compiler to partition and parallelize a second time on the backend. Which is what Rust does, basically, IIUC--a crate is nominally the LTO codegen unit, except to speed up compilation Rust has heuristics for partitioning crates internally for parallel LTO.
- dathinab 2y agocodegen units is quite the same as the `-j` flag cargo has the `-j` flag and defaults it to #cpus (logical cpus), so it's by default using the most times most optimal choice there And this will parallelize the compilation of all "jobs", roughly like with make. Where a job is normally (oversimplified) compiling one code unit into one object file (.o). And cargo does that too. The problem is that where rust and C/C++ (and I think D) etc. set code unit boundaries differ. In rust it's per crate. In C/C++ it's (oversimplified!!) per .h+.c file pair. This has drawbacks and benefits. But one drawback is that it parallelizes less good. Hence why rust internally split one "semantic code unit" into multiple internal code units passed to LLM. So this is an additional level of parallelism to the -j flag. In general this works fine and if people speak about rust builds being slow it is very rarely related to this aspect. But it puts a limit onto how much code you want in a single crate which people sometimes overlook. But in the OP article they did run into it due to placing like idk 100k lines of code (with proc macors maybe _way_ more then that) into a single crate. And then also running into a bug where this internal parallelization somehow failed. Basically imagine 100k+ line of code in a single .cpp file passing `-j` to the build to it will not help ;) I think one important takeaway is that it could make sense to crate awareness about this by emitting a warning if your crate becomes way to big with a link to a in-depth explanation. Through practically most projects either aren't affected or split it into crates way earlier for various reasons (which sometimes include build time, but related to caching and incremental rebuilds, not fully clean debug builds).
- wahern 2y ago> Hence why rust internally split one "semantic code unit" into multiple internal code units passed to LLM. And the same has happened in C and C++ land, albeit in the opposite direction, where multiple compilation units can be optimized together, i.e. LTO. See, e.g., GCC's -flto-partition option for selecting strategies for partitioning symbols for LTO. Also note that you can manually partition LTO in your Makefile by grouping compilation units into object files to be individually LTO'd.
- dathinab 2y ago> C and C++ land, albeit in the opposite direction, [...] LTO you also have that in rust to link different crates (and the internal splits for parallel compute) together
- sfink 2y agoI also use make -j for C++ with great success. And so I am also stuck with having to declare my functions in a separate file from where they're defined, and thus when stepping through code with a debugger or just a good source viewer that can jump to callers/callees, I never see the comments because they're in the header not the source. Not to mention the problems I run into when linking with a library that was compiled with different options or a different version of the source. And bending over backwards to implement concurrent algorithms to get decent runtime performance, and debugging the inevitable bugs that follow. Rust focuses on a different set of optimizations. I'm still working out when I prefer the Rust set or the C++ set or the Python set. I want to love Rust, but when doing exploratory work with unfamiliar APIs, the slow recompile loop and need to get everything correct for each incremental experimental build I do to try to figure out how something works is pretty painful. I don't know how much better it gets with familiarity. Rust is very nice when I fully understand the problem I'm solving and the environment I'm working in, and I vastly prefer it to C++ for that. But I frequently dive into unfamiliar codebases to make modifications.
- pjmlp 2y agoExcept the Rust ecosystem lacks the solutions we have in C++ land to compile fast and have easy paralelisation of builds. Because C and C++ communities for historical reasons embrace binary libraries, and binary component frameworks like COM, so while in theory a full build from scratch takes similar time as Rust, in practice that isn't the case. Also note that D, a language as complex as C++, with three compilers, one of them being based on LLVM, is largely faster to compile than Rust while using the LLVM backend, because Walter Bright did the right decisions on what to focus for development workflow.
- Ygg2 2y ago> Also note that D, a language as complex as C++ While D is complex, it's a different beast than Rust. I suspect think various checks, from lifetime to trait resolution, might make it more complex to parallelize than C++.
- steveklabnik 2y agoRust is already able to do more fine-grained parallel compilation than C or C++, at least in the codegen step. The "codegen units" concept doesn't work for those languages.
- wahern 2y ago> The "codegen units" concept doesn't work for those languages. Sure it does. Or at least could, depending on what you mean by that term. See, e.g., GCC's -flto-partition option, which supports various partitioning strategies for parallel LTO codegen.
- steveklabnik 2y agoLTO is distinct, and happens after what codegen units does. That said, it is a more fine grained parallelism, for sure. Rust does LTO as well as codegen-units. Really, as you gesture towards, on some level, this is all semantics: our linkers are also basically compilers too, at this point.