[HN Gopher] What part of Rust compilation is the bottleneck?
___________________________________________________________________
What part of Rust compilation is the bottleneck?
Author : dralley
Score : 100 points
Date : 2024-03-15 23:52 UTC (23 hours ago)
(HTM) web link (kobzol.github.io)
(TXT) w3m dump (kobzol.github.io)
| hobs wrote:
| Good article, just throwing out there that flamegraphs would be
| exactly what you need for visualizing this stuff.
| foota wrote:
| It looks like the profile they're build on already supports
| those, I think the intention here is to present a sort of at a
| glance view that could be quickly analyzed.
| hobs wrote:
| Makes sense. In my opinion I would just generate fake
| flamegraphs then - they are much more readable and the format
| is dummy easy to fake.
| kobzol wrote:
| Yeah, I actually generated these small charts out of a
| flamegraph, because it contains too much information and
| isn't easily split into three distinct parts. And once you
| condense the information into just 3 blocks, then using a
| flamegraph doesn't really add any further value, IMO.
| yu3zhou4 wrote:
| FWIW Jakub has more blog posts on his website which are also
| really interesting https://kobzol.github.io/
| AndrewDucker wrote:
| Could LLVM be speeded up by passing it the data in a more
| efficient structure? Or by slimming down the data it's passed in
| some way?
| Sharlin wrote:
| It's well known that the LLVM IR bytecode generated by rustc is
| terribly verbose - Rust constructs mostly get lowered very
| naively to a buttload of redundant IR, to be then pruned and
| condensed by the backend. This is by design, as it helps keep
| the frontend as simple and fast as possible, but there are
| certainly cases where it would be a net benefit to move some of
| the complexity to rustc in order to lighten LLVM's workload.
| davidhyde wrote:
| If you make a small change to your application, the Rust compiler
| does a significant amount of rework. That is, it recompiles a lot
| of code that it has already compiled before. There are valid
| technical reasons for this because of how LLVM works or that the
| linker needs to rewrite all addresses. Yes, incremental
| compilation is a thing but it's too coarse IMO. To me it seems
| that taking an extremely fine grained approach to compilation
| would improve the ergonomics of the iterative hack-and-run method
| of writing software. Some sort of local database of diffs or some
| such.
| makapuf wrote:
| Depends on your target; if you have tiny compilation units you
| won't be able to optimize /inline on a broad target, that's why
| single unit compilation is an option (that may or may not
| improve the resut)
| davidhyde wrote:
| This is true but what is really happening is that the
| frontend and backend cannot communicate intent effectively
| because of they way they are separated. The frontend doesn't
| know what is important for optimisation because that's not
| its job and the backend only sees the code the front gives it
| (never a wholistic view). So the easiest (and slowest)
| approach is to do everything over and over again.
|
| Increasing Codegen units (multi unit compilation) is just the
| user taking a risk that splitting things up will not affect
| performance optimisations. Nothing smart about it.
|
| If you had tiny compilation units and the frontend understood
| their significance to the backend then it would be able to
| build a graph of dirty code to be recompiled when a small
| piece of it changes.
| yxhuvud wrote:
| One problem they really need to address though is that as
| soon as you put everything in a single compilation unit, then
| compilation is single threaded and dog slow. Compilation
| units maybe make sense for C and C++, but for other languages
| they are just a way to structure the compilation. It should
| be automatic, and just better all around.
| KingOfCoders wrote:
| One reason for me moving from Rust to Go was compilation speed.
| Go is a simpler language, so apples to oranges, but Go compiles
| so fast, which to me makes development very different.
| makapuf wrote:
| That is definitely true, but from the article it's the backend
| that takes time, not the frontend where the language itself
| resides. If you compile go from llvm, it maybe as long as rust.
| KingOfCoders wrote:
| I wonder why we do not split up compilation more - especially
| for web developer. Rust does this a little with "check", C
| with "-O".
|
| I want fast compilation for my dev cycle or for unit tests, I
| want slow compilation with optimizations, escape analysis,
| correctness etc. for production (the distinction between a
| compiler and linter is also not clear, some compiles do what
| linters do in other languages).
| jokethrowaway wrote:
| We do, you have profile configuration for dev or for
| release
|
| You can get pretty big differences in terms of compile
| speed / binary size
| IshKebab wrote:
| Giving up entirely on the compile time of "performance"
| builds can be bad too though, e.g. for people writing
| games, audio software, etc.
| bluGill wrote:
| Only if you need to test the full game. If you can unit
| test algorithms and learn something then fast builds
| matter.
| GrumpySloth wrote:
| It's not just Rust. Practically every compiler based on LLVM
| is slow. Swift, Zig, Clang.
|
| The Go compiler being written from scratch based on the Plan9
| C compiler is a huge advantage.
| txdv wrote:
| Zig is moving away from LLVM. Its already has its own
| backend targeting debug builds for x86 and arm. ReleaseFast
| and ReleaseSmall is an entire different beast, but its
| going to be tackled eventually.
| rvdca wrote:
| I will chime in to day that rust is also building a
| alternative to the LLVM backend in the form of cranelift :
| https://github.com/rust-lang/rustc_codegen_cranelift
| kjksf wrote:
| Minor correction: Go compiler used to be a modification of
| Plan9 C compiler, then a Go port of that modification but
| then it was completely rewritten as a SSA-based compiler so
| today it has almost nothing to do with the original Plan9
| code.
| anonyfox wrote:
| second that. also another point: I write most Go code using
| only the stdlib, so there is no dependency web to take care on
| top of the actual code.
| KingOfCoders wrote:
| I also have much less dependencies with Go compared to other
| languages, especially in TS.
| pjmlp wrote:
| OCaml compiles just as fast without having to compromise on the
| type system.
| kjksf wrote:
| But then you have to compromise on speed of generated code,
| poor support of windows, number of libraries and overall
| ecosystem, no ability to generate standalone executables and
| probably more but I tried OCaml only briefly so can't speak
| to all of its shortcomings.
| pjmlp wrote:
| OCaml optimizations are certainly better than Go compiler
| that hardly does inlining and only recently got some PGO
| support, and those that care about using LLVM or GCC
| backends have to compromise on fronteds that still don't do
| generics.
|
| Go support on Windows is also not great, plugin package
| doesn't work, filesystem support assumes POSIX semantics,
| cgo requires installing mingw.
| sigsev_251 wrote:
| > using LLVM backends
|
| Wait, there is an LLVM based Go toolchain? I thought the
| Go crowd was known for their NIH obsession.
| pjmlp wrote:
| TinyGo.
| KingOfCoders wrote:
| Some see it as a compromising on types, I don't. After some
| years writing Scala code, trying to come up with even better
| types each day, Go to me is not a compromise but a relief.
|
| My love for types peaked when I was in my mid-40s, now that
| I'm 50+ I want simple things.
| margorczynski wrote:
| From my experience this simplicity is something you pay the
| price along the way - development is harder (a good typing
| gives you a lot of hints about functionality and puts
| bounds on developers on how to use it) and more error prone
| (less stuff gets caught by the type checker, instead you
| find it out in runtime).
|
| Of course it is good to be reasonable - some people
| completely fly off into the FP world and instead of
| actually building working stuff they think all day about
| some clever abstraction and types to model it.
| naasking wrote:
| I'd call that disillusionment with bad type systems, which
| are indeed unnecessarily complex. We have yet to achieve a
| typing "nirvana", but we're getting closer IMO.
| pjmlp wrote:
| I am almost 50, and my point of view on Go from 2012 has
| hardly changed.
|
| http://lambda-the-ultimate.org/node/4554#comment-71504
|
| At least it does generics now.
| sigsev_251 wrote:
| > At least it does generics now
|
| Not in gccgo
| jokethrowaway wrote:
| Over the years compile speed improved quite a bit (recently this:
| https://blog.rust-lang.org/2023/11/09/parallel-rustc.html made
| quite the difference)
|
| If we can squeeze more performance that's great but the largest
| concern I have around compilation is with the size of the target
| directory
|
| It can balloon up to node_modules levels
| kobzol wrote:
| There have been some recent improvements to this, but yeah, it
| can be still quite large. There is a WIP development of a
| garbage collector in Cargo that could help with this.
| chrismorgan wrote:
| I don't find node_modules exceeding even a few hundred
| megabytes very often. But Rust target directories can easily
| reach multiple gigabytes.
| pornel wrote:
| Apart from incremental compilation cache, a large chunk of it
| is debug information. Lowering debug info precision helps a
| lot (although it's still suspiciously large.)
| choeger wrote:
| Monomorphization.
|
| For every generic function f, rustc will generate as many
| instances as there are type instances (shape instances? Does the
| Compiler distinguish between different kinds of references that
| all get compiled to pointers?).
|
| This feature has a cost. Compare to OCaml's uniform object
| representation that enables comparatively blazing compilation
| performance but pays a prize in performance and weird FFI
| restrictions (integers with a tag bit).
|
| Btw. It's misleading to say "it's the backend" when the frontend
| is responsible for creating so much work for it.
| Alifatisk wrote:
| May I take the opportunity to ask, what's the reason for Metas
| somewhat heavy use of Ocaml? What's the appeal? You already
| pointed out the insane compilation perf.
| dermesser wrote:
| It's a pleasant and practical language to write, yet fairly
| safe.
|
| Imagine the safety of Rust but looking more like Python (or
| Haskell...), the concurrency of Go (since V5), and without a
| borrow checker (but a GC instead).
| davidmurdoch wrote:
| I watch some intro to OCAML videos, got excited about the
| languages features, then tried reading some real OCAML
| (Tezos, which was touted as the star of idiomatic OCAML
| projects - can't find the site that listed it now), and I
| found it so incredibly dense, hard to read, and almost
| completely devoid of meaningful naming and comments. It
| felt similar to reverse-engineering minified code to me.
| whateveracct wrote:
| sounds like unfamiliarity. OCaml isn't a language a
| layman can read without prerequisites.
| davidmurdoch wrote:
| I found the idea pleasant, but not in actuality. So maybe
| it's an acquired taste? Haha
| whateveracct wrote:
| oh it definitely is. A lot of Haskell can look like you
| describe, but it's perfectly legible if you have enough
| reps under your belt. I find normal languages hard to
| read nowadays.
| pjmlp wrote:
| Kind of, despite its slow builds fame, it is possible to have
| relative fast builds in C++ with monomorphization.
|
| By using binary libraries, external templates for common type
| sets, incremental compilation and linking, and nowadays (at
| least for VC++ already) modules.
|
| What Rust still lacks is having sound alternatives to LLVM, or
| someone supporting similar workflows in Rust.
|
| Using OCaml as an example, it is great to have multiple
| backends in the box, plus an interpreter, and pick and choose
| during development workflows.
| sigsev_251 wrote:
| Maybe cranelift will help with this. Faster compile times is
| one of its selling points.
| pjmlp wrote:
| That is something that I also look forward to.
| VHRanger wrote:
| D does the same thing with its template system and the
| compilation is still extremely fast.
| flohofwoe wrote:
| > ...when the frontend is responsible for creating so much work
| for it.
|
| That's the most important point I think. Clang compiling
| typical C code is very fast, but the same Clang compiling
| typical C++ code is very slow. Both use the same LLVM backend.
| zozbot234 wrote:
| Monomorphization can be manually addressed in Rust by writing
| generic function impls as delegating to a single function where
| the generic pameters are partily or fully omitted - a kind of
| "polymorphization". This is a pretty common pattern, e.g. in
| the Rust std library. In more recent versions of Rust this can
| be expanded via the use of const generics, e.g. to express the
| size and alignment of a generic type parameter, where the
| implementation only depends on these. So this kind of
| "polymorphization" can be applied more broadly.
| binary132 wrote:
| I'll never forget my first experience with Rust: trying to build
| the compiler from source and OOMing my cloud VM. :)
| xmcqdpt2 wrote:
| For libraries, the article shows most of the time being spent in
| "front end" phases. Isn't that a bit misleading, as the library
| will eventually have to be included in the final program's
| binary? The code generation phase isn't exactly attributable to
| any one module.
| ad-ops wrote:
| With all the recent improvements to compilation speed (nightly,
| cranelift, mold-linker), Rust has become much more pleasant.
| Trivial and incremental changes to a medium sized crate like
| rust-analyzer (~200k loc) takes around 2.5s and a small Axum
| project takes around 0.5s.
|
| These are my very subjective hobby benchmarks running archlinux
| on an AMD 9 7940HS.
|
| Of course the initial build or the release build take much
| longer, but it makes me hopeful for the future.
| andenacitelli wrote:
| Woah, 200k LoC is considered medium? I work at a Series A
| startup and our entire product (which is actually much more
| than a CRUD app) is only in the high tens of thousands, so
| that's just a funny thought for me.
|
| My theory is that because Rust is a low level language you tend
| to miss out on higher level primitives that promote more code
| reuse. Another theory is that Rust is mature but not quite as
| mature as something like Java, so there are fewer mature
| dependencies for you to delegate your work to.
|
| Thoughts on what's accurate? For context, I've written a bit of
| Rust myself, but am definitely a beginner.
| pornel wrote:
| Rust is very good at code reuse.
|
| Generics and cross-crate inlining enable zero-(runtime)cost
| abstractions, meaning there's usually no perf downside to
| using 3rd party code instead of your own.
|
| Strict type system, standardized error checking, thread
| safety in interfaces, and built in tooling for API
| documentation makes using libraries relatively easy.
|
| The ecosystem is pretty large now, and has a culture of
| respecting semver, and focus on safety and reliability.
|
| Cargo makes adding dependencies easy (the most common
| complaint is that it's _too easy_ , and people use too many
| dependencies).
___________________________________________________________________
(page generated 2024-03-16 23:02 UTC)