Hacker News new | ask | show | jobs
Rust Compile Times and Code Graphs (blog.danhhz.com)
45 points by jldlaughlin 977 days ago
2 comments

I really dislike the need to artificial decompose crates in this way. If Rust could accurately track dependencies at a more fine-grained level, then no one would do this because it adds complexity. It’s like optimizing assembly but for build systems.

What I mean by this is that if Rust tracked dependency information at the level of “my function too dependent on type declarations XYZ from crate A” vs “my function depends on the implementation of function in crate A”, then the Rust compiler would automatically apply this for you. It wouldn’t eliminate all dependency massaging, but it would eliminate the need for purely mechanical tasks and be more optimal (eg rearranging the internal fields of a crate shouldn’t dirty that dependency chain)

That could indeed be done (and I want to make that happen at some point), but you must be aware that this is an optimization, and as such trivial unrelated code changes can make the compile times balloon because all of a sudden you added a field to a struct with a type that used to fall on the division boundary, and you went from multiple pseudo-crates to a single one. This kind of behavior is hell to debug and figure out as a user.
I'm not sure I'm following the scenario described, but I don't see how the debugging complexity is more difficult than at the crate level. Can you help me understand why this might be the case? I'm not sure what you're calling as division boundaries / pseudo-crates here (are you suggesting that the dependency tracking won't be fine-grained & trivial changes can cause false sharing or something else?).

Doing all this tracking correctly though across layers of compilers is tricky if you want to shoot for absolute optimal performance. At the limit for truly optimal behavior, you'd track all the input information the compiler used when generating code and map it back to the source level (i.e. did I inline function A into function B - then I need to regenerate function A if B changes, but if no inline then don't regenerate A), which can be hard as most compiler optimization passes are information destroying. So yeah, I don't doubt that whatever "practical" middle ground is found as a realistic implementation can be tricky to reason about, but I suspect it almost doesn't matter if done well because it'll always outperform what you would have done by hand as outlined in this post / do it for you for free in 90% of cases.

He's saying that if you explicitly separate your crates out, and then you accidentally introduce a circular dependency you will get a compile time error. But if you have one big crate and rely on the compiler to detect internal dependencies for performance, then when you accidentally introduce a dependency that you didn't mean to it will happily compile but it will be slower so you probably won't notice and it will be really hard to debug.
2 minutes isn’t even really bad build times.

I think that part of “compile times suck” is that, when we are learning, we are building extremely small programs. Hundreds of lines at the very top end. During this phase of our personal development, we build a habit of writing a line of code, then compiling. Then changing an index, then compiling. Then realize we use a wrong variable, then compile. Then <some other minor thing> then compile.

Somewhere along the way, we get to needing to build thousands or hundreds of thousands of lines, and there’s very frequently no build up to this so our habit of making very minor change and then making the compiler tell us what’s wrong hurts.

Which is exactly what we need when doing GUI and games in Rust.

If it can't compete with the compile times from. NET, Java, Dart, Typescript then it won't be used by most folks.

C++ already lost the GUI wars for a reason, now stuck being the low level implementation language for GUI frameworks, driven by other languages.

IME Typescript generally has far worse compile times than rust especially if you need to start splitting into packages or do anything more complex than stripping type annotations.
It never took me 15 minutes, like on a Gtk+rs project I have.
Depends on what you're doing. If you're type checking, it's usually happening in the background via LSP. If you're "compiling" to JS, use esbuild and its instant.
Depends if that 2 minutes is full build or incremental..if incremental that is unacceptable, should be more like 2 seconds.

Compiling to see if you have syntax errors? What do you not have an editor?

Yeah, and cargo check exists for that very reason. It does type checking without actually compiling the code. Rust-analyzer, the LSP server for Rust, runs `cargo check` whenever the file is saved, IIRC, and then shows you what the compiler said.
Incremental type checking is still painfully slow on a large codebase.
if you have a reasonably good IDE you don't really need to explicitly compile the whole program outside of when you want to run it.
Which on any kind of graphical application is all the time.
Because that IDE is compiling the code behind the scenes... (Usually somewhere on a spectrum between literally just using the real compiler, or failing spectacularly to match it.)
I'm going to risk being a little nit picky here and I'm sure you mean it differently and just used some bad examples. You really shouldn't be relying on your compiler to tell you that you used the wrong variable, however, and if you are, then you would likely benefit immensely from getting some better tools. Some IDE's will help you a lot of the way, but great linting will frankly completely remove your "need" to check to see if your small changes actually work or not.