What I've heard is that the output that the Rust compiler gives to LLVM is much more complex than it needs be, and optimising this is the simplest way to reduce the time spent in LLVM. This seems plausible to me as there aren't many complaints that other compilers using LLVM are slow.
> there aren't many complaints that other compilers using LLVM are slow.
Practically every VM that tried to use LLVM as a JIT gave up as it was too slow. WebKit eventually abandoned their LLVM backend and wrote their own, B3.
JITs are of course a specialized use case, but there are also known issues with compilation speed in LLVM. For example, LLVM is single-threaded, so compiling a single very large compilation module can be much slower than it needs to be - and this is an issue hit by Rust due to how crates are designed. But you are also very correct that Rust is generating overly-complex LLVM IR for LLVM to compile, which is another problem there.
Also worth noting that while once clang beat gcc in compilation speed, as clang's optimizations have caught up to gcc the difference has vanished, and on many benchmarks today gcc compiles more quickly.
> Practically every VM that tried to use LLVM as a JIT gave up as it was too slow.
And it doesn't even seem to work effectively as a target for JIT. It can do the machine code emitting part of a JIT well, but that wasn't really the problem and it doesn't seem to work well at the real problem of optimising code for a high-level language. You need to optimise yourself, by writing basically your own compiler pipeline, before emitting LLVM.
See for example the Rubinius implementation of Ruby.
Right, but compilation performance is not a race in which you have to be first, but a minimum threshold that you must meet. Compilation just has to be fast enough that it doesn't annoy developers most of the time. Once you achieve that they'll mostly stop complaining. I believe Clang et al are fast enough.
(This is true for performance and optimisation is general: fast enough is good enough.)
Compiling larger projects, I certainly do not feel that Clang et.al. are fast enough. It takes minutes to compile stuff at work with 32 Xeon threads powering through at full load.
Once it takes seconds, we can talk about "fast enough".
It's mixed C/C++, and the codebase does not contain a lot of "heavy" features like templating and header-only implementations (I'm looking at you, boost).
Not the commenter you're responding to, but at my previous place I dealt with compile times of around 20 minutes from scratch, and 1-2 minutes incremental using Clang/LLVM, on a 20ish physical core Xeon with 128ish gigs of RAM.
No, I'm talking about a normal incremental build (which usually touch many things—I don't usually get to only recompile a single unit). If the planets align, I can get an incremental build to take just 30 seconds or so, but that's rare enough that I might mark such an event in my calendar. And that's on a beast of a server.
With Go, on my laptop, 30 seconds is the time it takes to perform a complete, optimized build of a medium-sized application, all dependencies and the standard library from source.
It's always so... frustrating whenever I go from a Go project back to our large C/C++ mixed codebase...
Because Go doesn't do very much optimization. They've been fighting back performance regressions every time they try to improve the performance of the resulting binaries.
Neither does clang, gcc or rustc unless you explicitly tell it to. I was talking about a normal development session. Why would I make compilation time worse by making optimized builds?
Not to pick on you or the parent comment, but this is a such a common mistake that it's worth correcting.
"Clang" is a thing not a person, therefore it's appropriate to use et cetera, not et alii. The former is for things, the latter for people (usually limited to authors of academic papers).
I Googled it, and the results were about evenly split on whether "et al." primarily stands for "et alii" (masculine) or "et alia" (neuter). But they agree that it can stand for either of those, as well as "et aliae" (feminine). In Latin, neuter nouns don't generally refer to people, so whether neuter is supposedly the primary meaning or just an option, it seems to mandate that the phrase can be used for things. On the other hand, the converse isn't true: neuter nouns don't refer to people, but masculine and feminine nouns frequently refer to things. In fact, the neuter gender only accounts for a relatively small fraction of Latin nouns. Thus, even "et alii" or "et aliae" would often be the correct choice in Latin for lists of things, depending on the type of thing. (Of course, there's no Latin word for "compiler", so there's no way to say which would be most appropriate in this particular case, unless you refer to the Vatican's book of Latin neologisms or something.) The word itself just means "other", with nothing inherently limiting it to describing people. In English, substantive adjectives (adjectives without nouns) do tend to default to people unless a noun has been specified: thus it's natural to say "compilers including Clang, GCC, and others", but not just "Clang, GCC, and others". But Latin likes to use substantive adjectives much more liberally. Especially in the neuter, I'm pretty sure "alia" is perfectly good by itself where in English you'd need to say "other things".
Anyway, you could argue that the use of "et al." in English has a restriction that doesn't come from the original Latin. Many of the Google results I found do claim that there's a convention of using "etc." for things and "et al." for people, although that seems to conflict with the idea that "et al." primarily stands for "et alia". But they mostly don't claim that that convention is a hard rule. And I don't think it makes much sense to establish one, considering the original meaning.
You may be right in terms of Latin, but now that both words are being used as English expressions, the original Latin can only be a guide, not a rule. In formal writing I'd try to get it "right" but informally if it doesn't make it harder to read, it's fine imho
Language evolves. The original authors' intent was conveyed much better, in my view, by 'et. al.' then it would have been by 'etc.' given the shift in how that latter phrase is used.
I interpreted that as a deliberate and sassy way to write. It's as if I said with Python et al. eating its lunch, Clojure better light a fire under its own ass.
This is a genuine case of survivorship bias in action.
I guarantee you that there are people not using Clang or writing, let's say, C++ because of slow compile times. You just never hear about how much they hate using them, because those people no longer are using them.
But that threshold isn't a constant across developers. Coming from the world of dynamic languages I still find Go's compiler to be annoyingly slow at times.
That really depends on your definition of "work". They certainly spend more clock cycles and produce more heat.
They might have some more comprehensive optimization passes eating extensive cycles, but even rustc debug builds are extremely slow in comparison to the Go standard compiler.
I'm not sure what a definition of "work" would look like where the Go compiler is doing as much "work" as the Rust compiler, but I expect it would be extraordinarily contrived. Go deliberately prioritizes compiler speed, and definitely skips complex optimizations that Rust or LLVM do, and Go is just generally simpler to compile, because the compiler will do nothing even remotely resembling the borrow checker, nor does it have complex types, and so on.
(One of my benchmarks for "Go has really made it" is when someone starts selling an optimized Go compiler that does the expensive optimizations. You could still ideally use the standard Go compiler to develop, but then you'd test and deploy with the slow optimized compiler.)
Complex optimizations do not apply to debug builds, so we are basically only comparing borrow checker and the likes.
However, then we can draw on two facts to kill that this should be a significant part of the issue: The post states that for most projects, the majority of the compile time is spent in LLVM, not Rust. Second (my memory may fail me here, but this is easy to measure), for larger projects, Go also smoke clang/gcc, which do not have Rusts fancy features.
But, as I stated in my parent comment, these cross-language, cross-compiler comparisons are quite difficult to make. This is also why I attack comments talking about one compiler doing more "work" than another. It cannot be directly compared.
All we know for certain, is that rustc is far too slow for our liking.
If you are implying that it is the "optimizing compiler" I mentioned, I have not seen good performance numbers that show it as being any better than the official Go compiler.
Were I going to start the company to produce the compiler, the question of "why isn't gccgo about 2x faster than Go?" is one of the first I'd examine, though. I personally have zero idea what the answer is. Or, indeed, if it's even still true. However, I am active enough in the Go community that if good benchmarks were going around I'd expect to have seen them. (They'd probably make it to HN.)
I rarely hear much about gccgo than what I hear in the Go release notes (usually "gccgo is now feature complete at <old version>"). I wonder if it still uses the plain thread implementation of goroutines like it did early on (which provides quite different performance characteristics)?
I am frankly too lazy to set up gccgo for comparisons again, but if I recall correctly, it takes considerably longer to compile whilst providing similar quality output. Thus, I would ask the opposite question that you presented:
If gccgo isn't notably faster than gc, why is compilation so much slower?
If one compiler spends more time generating identical quality output with an identical input language, it is hard to argue against the fact that there are wasted cycles at play. It may of course partly be due to an immature Go frontend. Who knows.
>Go deliberately prioritizes compiler speed, and definitely skips complex optimizations that Rust or LLVM do
As the OP pointed out, Rust debug builds are still much slower than Go builds, so lack of optimizations can't be a big part of the story. The simplicity of the language and the deliberate design of the compiler for speed seem to be the main factors.
You should have added the next bit after where you cut the quote off, which was "and Go is just generally simpler to compile, because the compiler will do nothing even remotely resembling the borrow checker, nor does it have complex types, and so on."
I'm not claiming this is true by any means, but it wouldn't surprise me that much that merely the work to tell if a bit of Rust code in, say, Servo, is legal Rust in the presence of a rich type system and the borrow checker and all the other such things going on is more work than it would be to compile the roughly-equivalent Go module entirely. Compared to Rust, Go does not so much "cut corners" as cut entire dimensions out of the picture, and then cut some more corners for good measure.
I cut it off because I was only disagreeing with the bit before the 'and'. Indeed, Rust is a more complex language and it is not surprising that it would take longer to compile. Although, as another poster pointed out, Go code also compiles much faster than plain C code (with gcc or clang), and C is of a similar order of complexity to Go.
>They might have some more comprehensive optimization passes eating extensive cycles, but even rustc debug builds are extremely slow in comparison to the Go standard compiler.
Work is not just the optimization.
Having Rust track lifetimes and warn about ownership bugs, races, etc, is also productive work for the compiler -- and happens during the debug builds too.
The post mentions that the majority of the execution occurs in LLVM, especially for optimized builds. In other words, anything Rust related has to be a smaller part of the compile time.
I stated in my parent comment that cross-language/cross-compiler comparisons is tricky exactly because of the vast differences in both frontends (lifetime tracking, warning generation) and backends (code generation, optimization). So yes, I know there are different amounts of work related with different languages.
However, I believe the compile time differences significantly outweigh the compile time differences.
>The post mentions that the majority of the execution occurs in LLVM, especially for optimized builds. In other words, anything Rust related has to be a smaller part of the compile time.
A, true, by the time it hits LLVM Rust's lifetime's analysis has already happened...
Polly is considered one of the "primary sub-projects of LLVM."
That said, it's generally not seen as part of the main stack, which would be LLVM, Clang, compiler-rt, and maybe lld, libc++, and libc++abi. (The latter three being fully optional replacements where most people use the standard system components instead).