12 ms·
A Few More Reasons Rust Compiles Slowly
- lukevp 6y agoWhat is “slow”? I mostly do JS and .NET, and I just started working through the rust hello world. I was shocked that I had an exe in around 0.2s. I haven’t worked on a large project so maybe this doesn’t scale, but of the 3 (at least for hello world which is fairly trivial) rust felt super fast. I saw mention of Roslyn so perhaps the issue is more around incremental compiles for IDE feedback? I didn’t see any issues with the VS Code language server (and I love the auto format on save, I set this up with prettier and I don’t want to go back to a place without auto formatters for everything. It’s great DX.)
- fsociety 6y agoI think it’s more of a question for compiling programs with LOC in the millions. The compile times for projects like LLVM, Chromium, and Tensorflow for example are disgusting.
- dkarlovi 6y agoWhat's the rough scale of "disgusting" here?
- sangfroid_bio 6y agoCompilation speed comparable to template heavy C++, a large difference compared to Go for similarly sized projects
- chubot 6y agoOne observation on LLVM Building LLVM from source, I was told in another forum, took 26 minutes across 16 cores on a top-end machine (ie. several hours on one core). I guess I don't need to build LLVM(?) https://old.reddit.com/r/ProgrammingLanguages/comments/hhxisw/how_practical_is_llvm_really/ https://old.reddit.com/r/ProgrammingLanguages/comments/hhxis...
- rapsey 6y agoChromium 5h+
- secondcoming 6y agoIt took my 4-core i4790K ~4 hours to compile tensorflow. Part of the build process builds LLVM...
- rurban 6y agoLike with Scala. Over 5 minutes for normal executable sizes.
- oldmanhorton 6y agoChromium will easily take 5 or 6 hours for a clean build on a good professional workstation (i7/i9/low end xeon, 32-64GB RAM, nvme ssd).
- floatboth 6y agoHow?! I can build LLVM, Rust and Firefox in less time on my R7 1700. How is Chromium so bloated??
- Bjartr 6y agoIs guess because Google devs have Google infrastructure to accelerate & paralellize builds in a distributed manner. Build time ends up being a pretty low priority to improve when the primary contributors to the project don't have a problem with it.
- sgerenser 6y agoThis guy did it in 53 minutes on a 16-core 3950x with a fast ssd: https://textslashplain.com/2020/02/02/my-new-chromium-build-pc/ https://textslashplain.com/2020/02/02/my-new-chromium-build-... Not fast by any means but big difference between 6 hours and 1.
- zozbot234 6y ago> I just started working through the rust hello world. I was shocked that I had an exe in around 0.2s. It's not everything. It's projects that use a lot of monomorphized generics, or compile-time macros, etc. And to be sure, this situation is improving over time - as new generics-related features are added, hopefully more of the redundant code that gets output when using these features will simply be cleaned up automatically, without adverse impact on compile times.
- tym0 6y agoDepends what you're building, I have what I would consider a small server app (Less than 10 JSON HTTP endpoints with some postgres), a dev incremental build takes 17.21s and a release build 7m 12s. A similar app in Go would probably take less than a second to build, they're obviously quite different languages but that doesn't feel that fast.
- xrisk 6y agoIs that because the libraries you link to are gigantic? Even so, the compiler shouldn’t be compiling those parts of the libraries that aren’t being used, right?
- tym0 6y agoActix, Diesel, Serde and their dependencies are definitely most of build time.
- pjmlp 6y agoSo far cargo doesn't do binary dependencies, so when you start a project, or do CI/CD, it compiles the all world from scratch.
- chc 6y agoI'm not intimately familiar with the internals of rustc, but my impression was that everything gets compiled and dead code is later eliminated. In order to avoid compiling unnecessary stuff, you generally need to use feature flags (e.g. you can say "only compile this if the serialization feature is enabled").
- cptskippy 6y agoI'm looking at the pipeline for smallish .NET Core Web App and the 6 projects build vary from 4-25sec each to build and with all the scaffolding it takes just under 5 minutes start-to-finish to produce binaries.
- qppo 6y agoPull in serde and run a non incremental release build
- YorickPeterse 6y agoTo illustrate, let's take the following VM code: https://gitlab.com/inko-lang/inko/-/tree/master/vm https://gitlab.com/inko-lang/inko/-/tree/master/vm. This VM consists of 18 262 lines of code, including VM tests. An incremental release build where nothing has changed (I just ran `touch src/lib.rs`) takes 25 seconds. Compiling the VM from scratch including all dependencies takes 1 minute and 28 seconds. That's not too bad, but it could (and in my opinion should be) much better. Debug builds are usually faster to compile, but tend to run too slowly to be useful for anything but debugging some VM bug.
- staticassertion 6y agobuilds for my 10kLOC project, which is a workspace. These are the commands run from the root of the workspace. We could clean up some deps, we use quite a few crates, and we use a number of proc macros as well. In general I'd say we're fairly "worst case" for compile time. cargo clean; cargo build; 4m20 Commenting out two lines of code in one of the services + cargo build; 0m14s cargo clean; cargo build --release 11m43s change 2 lines, cargo build --release 0m31s I don't find this egregious. ChromeOS Linux, which is debian Intel i7 4 core 32GB RAM
- kd5bjo 6y agoThe static linking section here feels a bit weak to me: if a Rust compilation fails, it usually happens before linking starts. On the other hand, if your program successfully compiles, do you really care whether the link time happens at the end of compilation or on program launch? Unless run-time linking is faster, they both result in the same delay between the end of code generation and the first execution of your code. Static linking says you pay that cost once, in the compiler, instead of every program launch, of which there will be at least one.
- chubot 6y agoThe work is not equivalent, and that's stated in the article: With dynamic linking the cost of linking is deferred until runtime, and parts of the linking process can be done lazily, or not at all, as functions are actually called Anecdotally Google's build system Bazel makes good use of the same thing for tests. The article also mentions that issue: That includes every time you rebuild to run a test. I don't remember the details since it was long time ago, but dynamic linking is a huge win here because of the lazy linking. The first function call is slow but the rest are fast. Loading is faster because you never call most functions. Rust seems to have a static dependency problem similar to Google's from 10 years ago (a big pyramid shape rather than using dependency inversion). ---- edit: related post about the Chrome/Ninja build: http://neugierig.org/software/blog/2012/07/gyp-toc.html http://neugierig.org/software/blog/2012/07/gyp-toc.html Here's one cool hack. You can, by flipping a flag, instead build this tree of libraries as shared objects. The result isn't something you'd ship to users, but it speeds up linking time during development considerably as you're passing smaller subsets of the files to the linker at a time. This leads to a cooler optimization. When you change one source file deep within the tree, naturally you need to rebuild its object file, then the library, and then rebuild any binary that depends on the library ... So instead, when building a shared object we write out both the shared object and "table of contents" file (generated with readelf etc.) that lists all the functions exposed by the resulting library. Then make dependent binaries only rebuild when the table of contents changes.
- zelly 6y agoBazel has rules_rust and supports distributed builds. It might be useful to switch to that instead of cargo.
- qppo 6y agoI have a rather large side project in Rust using cargo workspaces and compilation times do not bother me in the slightest, because I'm running incremental builds for everything. On the other hand I tried introducing Rust for a small part of a larger ecosystem and the cold compile times were so bad we rewrote the functionality in C. It shaved minutes off our CI build times, which costs actual money.
- asdkhadsj 6y ago> On the other hand I tried introducing Rust for a small part of a larger ecosystem and the cold compile times were so bad we rewrote the functionality in C. It shaved minutes off our CI build times, which costs actual money. Yea, we have this issue (as a shop now using Rust for all of our backend). I have a couple of hacks in place to cache the majority of the build thankfully, only needing to compile source code unless something changes. When our build cache works our builds take ~60s. When it doesn't, ~15m.
- qppo 6y agoTo be fair to Rust, this is certainly true of other languages. C++ builds can also be compile time hogs. What did you wind up doing to cache your builds? I've tried a few different hacks but none have stuck.
- pjmlp 6y agoIndeed, but unless you are doing some crazy meta-programming, you can keep reusing the same binary libraries, as inside the same company most projects tend to have similar build flags anyway. Alternatively it is also quite common to use dynamic libraries, or stuff like COM, XPC.
- jfkebwjsbx 6y agoC++ build times get high only if you start messing with the type system and ask for it to be recompiled every single time again and again. Rust is slower overall, which is why people tend to complain. And if you start messing around like in C++, then you get even crazier times. But in neither case it is a dealbreaker compared to other languages. Go proponents claim compilation speed is everything, which is suspicious. I do not need to run my code immediately if I am programming in C++ or Rust. And if I am really doing something that requires interactivity, I should be doing it another way, not recompiling every time...
- omn1 6y agoShameless plug from a fellow rustacean here. If anyone is looking for ways to improve compile times, I recently wrote an article with some tips: https://endler.dev/2020/rust-compile-times https://endler.dev/2020/rust-compile-times
- drej 6y agoThis article made rounds recently: speeding up Rust compilation by replacing/removing dependencies. https://blog.kodewerx.org/2020/06/the-rust-compiler-isnt-slow-we-are.html https://blog.kodewerx.org/2020/06/the-rust-compiler-isnt-slo...
- dang 6y agoDiscussed at https://news.ycombinator.com/item?id=23538220 https://news.ycombinator.com/item?id=23538220
- pjmlp 6y ago> I imagine the prior art is extensive, but a notable innovative responsive compiler is the Roselyn .NET compiler; and the concept of responsive compilers has recently been advanced significantly with the adoption of the Language Server Protocol. Both are Microsoft projects. Some examples of prior art in the context of strongly typed compiled languages with compilers designed for interactive development from the get go. Mesa/Cedar environment at Xerox PARC, and how Oberon and its descendants used to be integrated with the OS (Native Oberon and others) Energize C++ and Visual Age for C++ version 4, although quite resource intensive for their time. Eiffel, with its MELT VM on Eiffel Studio for development and AOT compilation system C and C++ compilers for deployment. C++ Builder and Delphi environments, although still batch, provide a similar workflow.
- gameswithgo 6y agoa responsive compiler is different than a fast one. rust has a good responsive solution in rust-analyzer now for ide features.
- pjmlp 6y agoWith the examples I gave, the application is ready to execute at any given moment, rust-analyzer is a crutch not a solution.
- barrkel 6y agoDelphi, when providing support to the editor, compiles the whole file containing the cursor but completely skips all codegen and in fact all function bodies (i.e. type checking, expression analysis etc.) that don't contain the cursor. The cursor is represented as a fake token, and the parser longjmps out with context when it's discovered; it resets the lexer context if it finds the cursor in the body of a function it's trying to skip. It doesn't need to compile dependencies because the public parts of units are a serialized symbol table with full definitions, rather than simple object files.
- pjmlp 6y agoThanks for the overview, I need to start collecting your comments. :)
- maxdo 6y agoIs it scala slow ?
- epage 6y agoFor build scripts, I've switched to avoiding build-time codegen and instead doing development-time code-gen, commit the results in, and have CI verify that the committed results and generator are not out of sync. This saves me building build-script dependencies, building build-script, and running running the build-script. In addition, by default, these are built and run in the same mode (debug/release) as the target binary, making build-scripts even slower when doing release builds. Wrote https://github.com/crate-ci/codegenrs https://github.com/crate-ci/codegenrs to help with this.
- swsieber 6y agoI like this approach. While I don't use it for any Rust programs I'm working on, I do use it for certain java reflection based things.
- epage 6y agoI'm working on trying to get my company to switch to this approach for our python monorepo. It frustrates me that I've never seen a good code-gen story for python - Some do runtime code-gen which is hard to learn from, debug, and verify - Some do install-time code-gen, slowing down install and and making it hard to inspect and verify - Some check codegen in but without any story to ensure it does not drift from the generator
- ajtjp 6y agoAs a relative neophyte in Rust (have gone through about half the chapters in the Rust Book), I recently deployed a small Rust server in DigitalOcean, and was surprised by the compilation speed. The server's code was about 27 KB in size, producing a binary of about 160 KB in size. But it had 92 dependencies, including transitive dependencies, and took 5 minutes and 38 seconds to build. Which was quite impressive relative to the size of the code, even allowing for the not-super-fast CPU. While I was watching its output, I realized that in Rust, all the dependencies are compiled; I'm used to Java+Maven or JavaScript+NPM where compiled dependencies are used instead, and that tends to be pretty quick (provided your network pipe is wide enough). I'd be curious to learn why Cargo re-compiles from scratch instead of offering pre-compiled binaries as well. I guess part of it is related to different target platforms, but it seems like if the top 5 platforms were targeted and had compiled resources available, you could reduce compile times for those platforms by a significant amount. On the other hand, the error messages I ran into along the way were quite good at pointing me in the right direction about how to fix them, which saved more time than the extra compile time cost, relative to the Python-based alternative I had been trying to set up before that.
- Elinvynia 6y agoWith Rust you get safety at compile time while avoiding memory safety issues at runtime. The compile times are objectively longer compared to most other languages. But whether this trade-off is worth it for you depends a lot on your company's CI pipeline and the skill of the developers.
- Gibbon1 6y agoI feel like the long compilation times is going to be a show stopper for smaller teams/companies where you can't afford the loss in productivity.
- efnx 6y agoAnecdotally I can tell you it does not amount to a loss in productivity. Cargo caches built deps so the cost is really only paid once in a while and the advantages of rusts type system are a boon to productivity.
- rubyn00bie 6y agoObviously just a band-aid for the symptom, and probably known by most, but FWIW sccache has been pretty freakin' great for me at keeping build times manageable: https://github.com/mozilla/sccache https://github.com/mozilla/sccache And, LOL, just now finally read the readme, didn't even know I could archive the the cache over the network.... #foreverN00b that's gonna be awesome.
- ChrisSD 6y agoRust supporter: Build times are comparable to C++. Rust critic: Build times are comparable to C++!
- uluyol 6y agoI wonder whether C++ modules will give C++20 the edge here.
- nindalf 6y agoI asked someone who worked on the C++ modules proposal and they said it might both help and hinder compile times. On one hand, compile time would be lowered because compilation units are larger and less can be done in parallel. But in future, incremental builds probably become quicker because it’s easier to tell what’s actually changed and what that affects.
- lenkite 6y agoNot sure if this is really true. Our C++ project builds are quite fast. I personally find Rust cold compile time far longer than C++.
- strictfp 6y agoIncremental development in Rust isn't particularly slow; it's compiling all the dependencies which takes the most time, and when you make changes you only recompile your local crate, which typically is quite fast.
- crazypython 6y agoD skips generating a high-level intermediate representation. (HIR) D has its own, mature version of Cranelift, the DMD backend, which generates fast codegen. Furthermore, like Rust, D has a mature LLVM backend. When compiling D programs with DMD and LLVM in -O0, LLVM only took slightly longer.
- uvatbc 6y agoShameless plug: We just started supporting rustc compiler caching and released it as part of our support for open source projects: https://crave.io/#opensource https://crave.io/#opensource We're showcasing the Libra project to start with and would be up for adding other projects that the community is interested in.
- pdimitar 6y agoI'd love to know more, f.ex. you could download and compile the top 100 downloaded Rust crates, demonstrating compiled times with and without Crave.
- floatboth 6y ago> In the same experiment I did to calculate the amount of build time spent in LLVM, Rust spent 11% of debug build time in the linker Which linker?
- ndesaulniers 6y agoThis is a good question; LLD blows the doors off BFD and GOLD IME.
- Caitin_Chen 6y agoThis series has 4 episodes. Episodes 1-3: - Episode 1: The Rust Compilation Model Calamity https://pingcap.com/blog/rust-compilation-model-calamity https://pingcap.com/blog/rust-compilation-model-calamity - Episode 2: Generics and Compile-Time in Rust https://pingcap.com/blog/generics-and-compile-time-in-rust https://pingcap.com/blog/generics-and-compile-time-in-rust - Episode 3: Rust's Huge Compilation Units https://pingcap.com/blog/rust-huge-compilation-units https://pingcap.com/blog/rust-huge-compilation-units
- zamalek 6y ago> rustc is notorious for throwing huge gobs of unoptimized LLVM IR at LLVM and expecting LLVM to optimize it all away. It might be a good idea to implement a cheap/fast optimization phase according to rustc assumptions, specifically for rustc, to clean up the mess. Keeping the rustc emit layer concise and clear should really be a goal, in my opinion. Obvious code is easier to audit.
- efnx 6y agoBut cargo caches well, and after that first compile subsequent compilations will take mere seconds.
- int_19h 6y agoWhat I don't get is why this: > rustc is notorious for throwing huge gobs of unoptimized LLVM IR at LLVM and expecting LLVM to optimize it all away. is seen as a problem with Rust, rather than LLVM. Isn't the whole point of having something like LLVM to have a high-quality backend where all optimizations only have to be implemented once, and then they just light up for all the languages that target it?
- johnlorentzson 6y agoSome things are best optimized by the actual compiler.