← Back to context

Comment by aliasxneo

13 hours ago

Weird take. I've worked in large Rust projects for half a decade (in one case, a full blockchain written in Rust). It's slow, full stop. I was under constant pressure to keep CI times down and was resorting to some of the craziest optimizations just to save a few minutes. Meanwhile, all of the platform code I was writing in Go almost never had a CI time above a few minutes.

Doesn't mean one is better than the other, but to find it odd someone had an experience with slow Rust compiliations and then blame it on their setup is a bit odd.

It is slow. Nobody would deny that. It also requires work to avoid disastrous compile times, that's also true.

But 15 minutes? Even rustc doesn't take that long (on a powerful computer), and that's a very large program that is compiled at least twice (bootstrapping)! That's just poor engineering, not knowing how to write fast-to-compile Rust code.

  • You can't compare rustc to a large application. Yes it's a large program, no it's nowhere near as large as Codex CLI. 1420 crates.

    I just did a release build of Codex CLI with "lto = false" for codex CLI binary and code mode host only, and it took 75 minutes, of it 1063 seconds for the final rustc of codex bin. Windows Defender off. That's on a 4-core 2021-era Intel laptop with 32GB RAM and a half-TB SSD. And I can't let the build run at default concurrency, because rustc OOMs the machine, so this is with cargo jobs = 4. Regardless, the final rustc call is independent of that and that was 1063s with lto=false. Previous build was 90 minutes with default thinlto for the same CLI build.

    How can Codex write fast-to-compile Rust code, do tell?? Do tell!

    • Behavior-wise, it does a lot more than the Codex CLI. The code of Codex is just of extremely poor quality. Just glanced, it looks like it uses at least two different HTTP server libraries (axum and tiny_http)! From quick check, rustc has 44 duplicate transitive dependencies (with different versions) while Codex has 109! And 2 dependencies are duplicated five times (in rustc the most duplicated are only three times)!

      > How can Codex write fast-to-compile Rust code, do tell?? Do tell!

      Like a human can, I suppose? Avoid extreme usage of generics. Avoid dependencies if not needed. Make sure you synchronize dependency versions to avoid duplicate code. Etc..

    • Also, Linux has (often significantly) faster compile times than Windows (due to filesystem performance, even with Dev Drive).

What kind of Rust projects? How many dependencies? Does it use macros? How about generics and traits?

Most of the Rust ecosystem makes unnecessarily heavy use of compile-time traits and monomorphized generics (aka Go generics or C++ templates). The language syntax encourages it as well. The thing is compile times will NEVER drop to level of Go unless the entire set of players (language, standard library, almost all library developers) actually prioritize dynamic dispatching instead of static dispatching / monomorphization. That's the tradeoff for the performance and RAM usage you gain. Template-heavy C++ also suffers from the same issues for the same reason.

Golang is fast to compile because its interface definitions are runtime/dynamically dispatched (aka `&dyn Trait` in Rust or virtual classes in C++) and it had no generics for a long time forcing types like slices, map and vectors to be magic types.

If you need fast compile times, you need to split your program into multiple binaries or write your own backend ecosystem that almost only uses dynamic dispatching. If your full Cargo dependency tree pulls 50+ crates, you'll not get fast compile times. You need to avoid derive-macros like plague. You need to use non-Cargo build systems or `build.rs` to generate Rust code instead of derive-macros.

Unlike Golang Rust also targets extremely memory constrained devices where monomorphization enables memory savings, extra optimizations and fewer CPU cycles / instructions (e.g. microcontrollers, UEFI/BIOS implementation). It is a system programming language first. With system programming languages you are responsible for these choices as the developer. Rust just happens to have a vivid scene of backend libraries since the language's safety enabled many non-system developers to write correct and performant programs quickly.

  • This is a bit of a tangent, but Go generics aren't monomorphized. Each instantiation is "stenciled" down to its GC shape and then the differences among types with the same shape are handled using a "dictionary" which is kind of like Swift's witness tables or Go's own interface vtables. See this description for details: https://github.com/golang/proposal/blob/master/design/generi...

    The typical end result sits somewhere between monomorphization and type erasure in terms of generated code and runtime indirection.

Well, me too, and I can confidently tell you I’ve never seen build times in the 10+ minute range (on capable machines).