18 ms·
How to speed up the Rust compiler in 2018
- oblio 8y agoIs there any dedicated effort to speed up LLVM compilation? I imagine that would benefit a lot of languages.
- noelwelsh 8y agoWhat 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.
- arghwhat 8y agoThat depends on what you are comparing to. Both GCC and Clang are very slow compared to, say, the Go standard compiler. However, comparing compilation speed across languages and compiler implementations is a bit difficult.
- banachtarski 8y agoThey also do a lot more work.
- arghwhat 8y agoThat 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.
- jerf 8y agoI'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.)
- foldr 8y ago>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.
- jerf 8y agoYou 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.
- pcwalton 8y agoThe Go compiler cuts out nearly all the optimization that LLVM does.
- noelwelsh 8y agoRight, 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.)
- arghwhat 8y agoCompiling 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".
- noelwelsh 8y agoTry using Scala :) Is that minutes for every compile, or for an (infrequent) full compile?
- pdpi 8y agoNot 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.
- iClaudiusX 8y agoNot 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).
- coldtea 8y ago>Both GCC and Clang are very slow compared to, say, the Go standard compiler. Well, the Go standard compiler doesn't do much.
- brighteyes 8y ago> 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.
- chrisseaton 8y ago> 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.
- shakna 8y agoPolly [0] is LLVM's optimisation project, at least for memory access. It's a fairly active project. [0] http://polly.llvm.org/ http://polly.llvm.org/
- oblio 8y agoI should have clarified before: I meant LLVM compilation speed, no the speed of the code generated by LLVM :) I've edited my original comment to include the clarification.
- shakna 8y agoAh, Polly actually slows compilation speed. [0] [0] http://polly.llvm.org/docs/Performance.html#compile-time-impact-of-polly http://polly.llvm.org/docs/Performance.html#compile-time-imp...
- fooker 8y agoPolly is not part of LLVM, really.
- jcranmer 8y agoPolly 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).
- MikkoFinell 8y agoWhy is Rust so popular on HN? Honest question.
- jannes 8y agoProbably because of its memory safety, which enables easier parallelisation of workloads among other things.
- MikkoFinell 8y agoCouldn't you achieve the same thing in C++ by using message passing between threads instead of shared memory? That way, you would get safe parallelisation without having to fight the borrow checker in every part of the program. I understand that having safety statically enforced at compile time is a real gain in terms of eliminating that class of bugs. However, the mental cost a Rust programmer pays for that trade (due to borrow checker) seems too high for me if you can get the same thing simply by shifting your programming patterns slightly.
- heinrich5991 8y agoNo, you can't. It's either performance or bugs – look at the big C++ projects.
- exDM69 8y ago> However, the mental cost a Rust programmer pays for that trade (due to borrow checker) seems too high for me In C++ land, you'd have to use TSan thread sanitizer and a lot of other tooling (ASan, Valgrind, etc) to achieve even close to the same level of confidence. Not only is this a runtime check but all that tooling has a fairly heavy mental cost too.
- danieldk 8y agoCouldn't you achieve the same thing in C++ by using message passing between threads instead of shared memory? I know that the parent post used multi-threading as an argument in favor of Rust. But there are many ways to blow of your foot in single threaded C++. For example, consider this single-threaded code: std::vector<size_t> v; v.push_back(1); size_t *first = &v[0]; std::cout << *first << std::endl; // The STL vector implementation will probably // reallocate the backing array at least once. for (size_t i = 2; i < 1000; i++) v.push_back(i); std::cout << *first << std::endl; You wouldn't be able to introduce the same error in Rust. Once you borrow the an element of a Vec immutably, you cannot create mutable borrows during the lifetime of any immutable borrows. So the compiler would reject pushing new elements. However, the mental cost a Rust programmer pays for that trade (due to borrow checker) seems too high for me The thing is that most of the errors that the borrow checker will point out are potential programming errors in C or C++ as well. However, current C/C++ compilers will put most of the responsibility completely on you (plus tools like Valgrind). So, to me C++ has at least the same cognitive load if you want to program responsibly.
- jeffdavis 8y agoA recent good experience with rust: wanted to learn a little about machine learning. Found a crate called rusty-machine, copied some examples from the docs, and built them. No problems, compiling all of the dependencies was fast, examples worked, and in literally about 2 minutes I had a neural net and was hacking on it. This is without touching rust in months.
- orib 8y agoGreat insights about improving the rust compiler performance! What else would you do to speed it up?
- leshow 8y agoIs there a reason why you'd create 2 distinct repositories instead of just creating branches and adding new remotes?
- lambda 8y agoSo that you can easily rebuild and re-run each one with difference in build settings, instrumentation, etc, and get comparisons between the baseline and the modified version, without having to check out a different branch each time. Sometimes it's just quicker to work with two separate working directories than switching branches in one.
- ben0x539 8y ago`git worktree` is pretty neat btw!
- jswrenn 8y agoI cannot speak for OP, but I work on the Pyret compiler. Building a bootstrapping compiler is a lot of work and creates many (intentionally) untracked files. Pulling the repo twice allows me to hack on my fork without doing a full-rebuild every time I need to try something or change something on master.
- nnethercote 8y agoBecause my git-fu is weak and I have a fast network connection and a big disk! Doing things the dumb way works fine for me :)
- ajross 8y agoSo... grumpy old man response here: These are all tiny, targetted microoptimizations worth a percent or three of benefit in specific tests. They're worth doing (or at least evaluating) in any mature product and I have no complaint. Nonetheless rustc remains really slow relative to other similar technologies, including C++ compilers. Is there any consensus as to why? I mean, with C++, the answer is something to the effect of "template expansion happens syntactically and generally has to be expressed in headers, leading to many megabytes of code that has to be compiled repeatedly with every translation unit". And that isn't really amenable to microoptimization. We all agree that it sucks, and probably can't be fixed with the language as it's specified, and chalk it up to a design flaw. What's the equivalent quip with rustc? I mean... is it going to get faster (in a real sense, not micro), or is it not? Is this fixable or not, and if not why?
- bbatha 8y agoThe equivalent is doing more dead code analysis at the MIR stage before sending it to llvm. This is where rustc loses most of its perf. LLVM is usually 50-60% of execution time, mostly because rust sends teams of ir to chew through.
- ajross 8y agoSerious question: is there any reason dead code detection would be any faster at the syntax level than the generated code? I mean, I'm no compiler expert but in a straightforward SSA representation "dead code" is just a register that gets assigned but never used, all of which can be detected and dropped globally with just one pass over the data. I suspect the real reason is going to turn out more complicated, no? Like, the code is really "dead" yet because we can't know if it will be used later without solving the halting problem. Which, if you think about it, is pretty much exactly the situation C++ is in. All those expanded templates aren't "dead" yet at the point of expansion either, you just don't know if the code will use them. And in practice it won't, so you wasted a ton of cycles to find that out.
- saosebastiao 8y ago
- mastax 8y agoIf you're interested in the current "grand plans" for performance, check out this issue: https://github.com/rust-lang/rust/issues/48547 https://github.com/rust-lang/rust/issues/48547 The query parallelization work has been coming along. Also it was recently discovered that building LLVM with a recent clang and doing cross-language LTO speeds up builds by ~25%.
- shmerl 8y agoIs rustc still using forked llvm, or it's already switched to upstream?
- mastax 8y agoNothing's really changed with that recently. Still using a fork (though less divergent than it has been?), but it also builds fine with upstream.
- shmerl 8y agoAre there plans to merge remaining differences to upstream and switch to it?
- steveklabnik 8y agoWe generally send our patches upstream but they can take some time to land. This will just always be the case. We test that everything builds with stock LLVM, you’ll probably just get some test failures from the unpatched bugs.
- shmerl 8y agoSo the fork is mostly needed for convenient fast iteration?
- steveklabnik 8y agoYup.
- Rafuino 8y agoTotal noob question here, but how does the hardware you're using affect compilation speed? What's the baseline hardware being used in this benchmark he runs? There's no way to recreate the benchmark environment unless we know this. When someone like ajross says rustc is really slow, how slow are we talking?
- steveklabnik 8y ago> how slow are we talking? It really depends on your machine, as you ask above. And on your project. And if it’s a fresh build or a rebuild. And all sorts of stuff. It’s slow enough that improving this is a priority for us.
- Rafuino 8y agoThanks, Steve, for the response. Certainly there are a lot of variables so I'd like to see benchmark reports actually disclose what's under the hood. Let's say, for example, you're on a regular rebuild cadence for your work. What makes the biggest difference for speed in such a scenario: CPU cores, CPU speed, DRAM speed, SSD latency, SSD throughput, something else?
- steveklabnik 8y agoI only own one computer so I can’t really get empiric data here. We do track compile times on the same machine at perf.rust-lang.org, but I do t believe anyone has done those kinds of tests. It’d be neat to have though!
- Rafuino 8y agoAwesome. I've been checking out the site on the wall time submissions especially. To the point of the original questioning, do you know what's under the hood on the machine you're using to test at perf.rust-lang.org? I'm not seeing it called out on the site so far in my searching...
- 8y ago