On topic: I agree that upstream-provided 3rd party binaries are not a good solution to this particular problem. That is the type of solution that works in a closed environment like a corporate CI system, but it should not be the default.
Off topic: I don't understand why the article repeatedly says the Rust compiler or procedural macros are already fast, even "plenty fast". Aren't they slower or about as slow as C++, which is notorious for being frustratingly slow, especially for local, non-distributed builds?
"Plenty fast" would mean fast enough for some simple use-case. e.g., "1G ethernet is plenty fast for a home network, but data centers can definitely benefit from faster links". i.e., fast enough you won't notice anything faster when doing your average user's daily activities, but certainly not fast enough for all users or activities.
The Rust compiler is then in no way "plenty fast". It has many benefits, and lots of hard work has gone into it, even optimizing it, but everyone would notice and benefit from it being any amount faster.
Ehhhhhhhh. Are you talking full build or incremental? How long did it take?
Clean and rebuild of Unreal Engine on my 32-core Threadripper takes about 15 minutes. And incremental change to a CPP takes… varies but probably on the order of 30 seconds. Their live coding feature is super slick.
I just cloned, downloaded dependencies, and fully built Symbolicator in 3 minutes 15 seconds. A quick incremental change and build tool 45 seconds.
My impression is the Rust time was all spent linking. Some big company desperately needs to spend the time to port Mold linker to Windows. Supposedly Microsoft is working on a faster linker. But I think it’d be better to just port Mold.
My 32 core threadripper builds ue5 in 12 minutes on Windows. Single file changes on our game are usually under 10 seconds due to unity builds, and a good precompiled header.
My first clone on symbolicator took the same length of time on my windows machine. Even with your numbers, 4 minutes to build what is not a particularly large project is bonkers. I
My experience across a wide range of C++ projects and wide range of Rust projects is that they’re roughly comparable in terms of compilation speed. Rust macros can do bad things very quickly. Same as C++ templates.
Meanwhile I have some CUDA targets that take over 5 minutes to compile a single file.
I feel like if Rust got a super fast incremental linker it’d be in a pretty decent place.
No, threadrippers[0] are commonly used by people working in large compiled codebases. They're expensive, sure, but not incomparable to a top of the range Macbook Pro.
> No, threadrippers[0] are commonly used by people working in large compiled codebases.
I don't think that's true at all, unless you're using a very personal definition of "common".
In the real world, teams use compiler cache systems like ccache and distributed compilers like distcc to share the load through cheap clusters of COTS hardware or even vCPUs. But even that isn't "common".
Once you add CICD pipelines, you recognize that your claim doesn't hold water.
I know, I have one and it cost the company about 3x as much as a 16" MacBook Pro. An expense that's very unaffordable for most companies, not to mention most developers.
(Even most MBPs are unaffordable for a large set of developers.)
I don't think it's as accessible to average C++ or Rust developers as you expect.
I've also got a 14" Macbook Pro (personal machine) that was a _little_ cheaper - it was £2700.
> An expense that's very unaffordable for most companies
I think it's unaffordable for some companies, but not most. If your company is paying you $60k, they can afford $3500 once every 5 years on hardware.
> I don't think it's as accessible to average C++ or Rust developers as you expect.
I never said they were accessible, just that they are widespread (as is clear from the people in this thread who have the same hardware as I do).
FWIW, I was involved in choosing the hardware for our team. We initially went with Threadrippers for engineers, but we found that in practice, a 5950x (we now use 7950x's) is _slightly_ slower for full rebuilds but _much_ faster for incremental builds which we do most of.
It’s definitely not a baseline. It’s simply what I have infront of me.
Lenovo P620 is a somewhat common machine for large studios doing Unreal development. And it just so happens that, apparently, lots of people in this thread all work somewhere that provides one.
I don’t think the story changes much for more affordable hardware.
I kind of does, given that the C and C++ culture depends heavily on binary libs (hence the usual ABI drama), in more affordable hardware building everything from source, versus using binary libraries makes a huge difference, thus C++ builds end up being quite fast unless they abuse templates (without extern template on libs).
What can I use to cache with MSVC that isn't Incredibuild? (a per-core license model isn't suitable - we'd spend more on incredibuild licenses every year than we do on hardware)
Also, I've spent a _lot_ of time with Unreal and the build system. Unreal uses an "adaptive unity" that pulls changed files out of what's compiled every time. Our incremental single file builds are sub-10-seconds most of the time.
Lack of Precompiled Header support kills this for us immediately. (We also currently use the unsupported method of debug info generation which we could change). A local build cache is no better than UnrealBuildTool's detection though.
> Lack of Precompiled Header support kills this for us immediately.
Out of curiosity, why do you use precompiled headers? I mean,the standard usecase is to improve build times, and a compiler cache already does that and leads to greater gains. Are you using precompiled headers for some other usecase?
> and a compiler cache already does that and leads to greater gains
Can you back that claim up? I've not benchmarked it (and I'm not making a claim either way, you are), but a build cache isn't going to be faster than an incremental build with ninja (for example), and I can use precompiled headers for our common headers to further speed up my incrementals.
You did encourage me to go back and look at sccache though, who have fixed the issues I've reported with MSVC and I'm going to give it a try this week
Additionally, in Windows, when Rust is compiling the Microsoft's linker allays launch the M$ telemetry vctip.exe, that stablish an internet connection [Here is an icon of someone in sad thought].
If anyone knows a method for to avoid such launch (besides connection blocking after launch by firewall ), share it please.
> On topic: I agree that 3rd party binaries are not a good solution to this particular problem. That is the type of solution that works in a closed environment like a corporate CI system, but it should not be the default.
Why?
I mean, OS distributions making available whole catalogs of prebuilt binaries is a time-tested solution solution that's in place for decades. What leads you to believe that this is suddenly undesirable?
I think there's a very obvious distinction between the kind of "3rd party" that is your chosen OS distribution and the kind of 3rd party that is "some person on github or crates.io", and I didn't feel the need to clarify this as I thought it was obvious.
I've clarified it to "upstream-provided 3rd party" in the original post.
It's a 3rd party package that rustc and cargo already internally depend on themselves. If you're okay getting those distributed to you as binaries, this package too may as well come with a precompiled binary.
Either way you're running their code, I don't understand the outrage here, frankly. People who look at the code they're running and the changes they're pulling in of course noticed this, but I seriously doubt even 1% of the rageposters are in that set of rust users.
I'm not outraged. I dislike it, and it would be a "con" when considering any use of the project.
Binaries are relatively inscrutable, and there are massive differences in ease of doing something nefarious or careless with OS binaries, and upstream checked in source code, and upstream checked in binaries.
That's just one or two reasons why I think this approach is not at all commonplace in open settings, and is generally limited to closed and controlled settings like corporate environments.
A more typical and reasonable approach would be having a dependency on such a binary that you get however you like: OS, build from upstream source, upstream binary, etc., and where the default is not the latter.
And now if you allow me to get a bit off-topic again: to me, having to "innovate" like this is a major sign that this project knows it is very slow, and knows it's unlikely to ever not be very slow.
> I think there's a very obvious distinction between the kind of "3rd party" that is your chosen OS distribution and the kind of 3rd party that is "some person on github or crates.io"
That tells you more about crates.io than consuming prebuilt binaries.
Some Linux distro ship both source code packages and prebuilt binaries. This has not been a problem for the past two decades. Why is it suddenly a problem with Rust's ecosystem?
crates.io is just a platform for people to make their Rust code distributable; it's not curated in any way really (other than removing obvious malware). An OS distribution is explicitly curated.
I never saw anyone complain that crates.io isn't curated. Why is this suddenly an issue when discussing shipping prebuilt binaries? If it's somehow ok to download the source code as-is, why is it a problem if crates.io builds it for you?
Some problems have solutions, but people need to seek for solutions instead of collecting problems.
I'm not complaining that it's not curated, specifically because it's exclusively a source code package directory. Auditing source code for safety is hard, doing so with a binary is much harder.
I wouldn't mind if rust had reproducible builds, and the binaries had to be built+signed by both the original author and crates.io. But how the article describes it seems sketchy
Because it demonstrates a failure of the Rust ecosystem.
Proc macros have effectively become fundamental to Rust. This is unfortunate, but, given that it is reality, they should get compiler support. Part of what is making them so slow is that they need to process a firehose of information through a drinking straw. Placing proc macros into the compiler would not only speed them up but also remove the 50+ crate dependency chain that gets pulled in.
Serde, while slow, has a more fundamental Rust problem. Serde demonstrates that Rust needs some more abstractions that it currently does not have. The "orphan rule" means that you, dear user, cannot take my crate and Serde and compose them without a large amount of copypasta boilerplate. A better composition story would speed up the compilation of Serde as it would have to do far less work.
Rust has fundamental problems to solve, and lately only seems capable of stopgaps instead of progress. This is unfortunate as it will wind up ceding the field to something like Carbon.
The orphan rule isn't a problem that needs solving, it is itself a solution to a problem. There's nothing stopping Rust from removing the orphan rule right this minute and introducing bold new problems regarding trait implementation coherence.
Edward Kmett has a great explanation[1] of the considerations around this space. It's not that alternatives to the orphan rule are hard to implement, it's that they lead to things being much harder to reason about for the programmer.
You have to pulverize the macro into syntax atoms and then reassemble it piece by piece into an AST that the compiler needs. All the processing to do all of that should be a part of the compiler--not a smattering of 50 crates (syn being the most oppressive one) and a weak compiler API.
Serde has to enumerate the universe even for types that wind up not used. Those types then need to be discarded--which is problematic if it takes a non-trivial amount of time to enumerate or construct them (say: by using proc macros). Part of this is the specification that must be done because Rust can't do compile-time reflection/introspection part of which is blocked because of the way Rust resolves types--different versions of crates wind up with different types even if the structures are structurally identical because Rust can't do compile time reflection and relies on things like the Orphan rule.
I am not saying that any of these things are easy. They are in fact hard. However, without solving them, Rust compilation times will always be garbage.
I'm beginning to think that the folks who claimed that compiler speed is the single most important design criterion were right. It seems like you can't go back and retrofit "faster compiler time" afterward.
> You have to pulverize the macro into syntax atoms and then reassemble it piece by piece into an AST that the compiler needs. All the processing to do all of that should be a part of the compiler--not a smattering of 50 crates (syn being the most oppressive one) and a weak compiler API.
First of all it's not 50 crates, it's 3 main crates syn, quote and proc-macro2 and a few smaller helper crates which are used less often.
And the reason to not include them in the compiler is exactly the same reason why "crate X" is not included in std. Stuff in std must be maintained forever without breaking changes.
Just recently syn was bumped to version 2 with breaking changes which would have not been possible if it was part of the compiler.
In any case, shipping precompiled proc macros WASM binaries will solve this problem.
> Serde has to enumerate the universe even for types that wind up not used. Those types then need to be discarded--which is problematic if it takes a non-trivial amount of time to enumerate or construct them (say: by using proc macros). Part of this is the specification that must be done because Rust can't do compile-time reflection/introspection...
Yes, it's a design tradeoff, in return Rust has no post-monomorphization errors outside of compile time function evaluation. I think it's worth it.
> I'm beginning to think that the folks who claimed that compiler speed is the single most important design criterion were right.
I don't agree. As long as the compile times are reasonable (and I consider Rust compile times to be more than reasonable) other aspects of the language are more important.
Compile time is a tradeoff and should be balanced against other features carefully, it's not the end goal of a language.
> First of all it's not 50 crates, it's 3 main crates syn, quote and proc-macro2 and a few smaller helper crates which are used less often.
That may be technically true. However, every time I pull in something that uses proc macros it seems my dependency count goes flying through the roof and my compile times go right out the door.
Maybe people aren't using proc macros properly. That's certainly possible. But, that also speaks to the ecosystem, as well.
> seems my dependency count goes flying through the roof and my compile times go right out the door.
I have two dependencies (memchr, urlencoding) and one two dev dependencies (libmimic, criterion). I have 77 transitive dependencies, go figure. People like to outsource stuff to other libraries, when possible.
I hope you are correct about predictions because I really want to see the day that C++ gets a spike put through it because something better exists.
I really don't want to see something like Carbon take off. It's not really that much "better" than C++, and it will take up the oxygen from something that could be a genuine replacement.
Definitely, as C++ allows using binary libraries from the get go, instead of rebuilding the world after each checkout, or clean build on the CI/CD pipeline.
There are workarounds like sccache, but again additional tooling to take into account, across all target platforms.
I think there's a different kind of "plenty fast" that more closely aligns with "as fast as it can be." If you develop some code that runs at 90% of the theoretical throughput, I'd call that plenty fast, even if the theoretical wait time was 100s of seconds. The rust compiler does a lot of stuff that simpler languages don't do. I would accept that level of checking to take longer while still being considered "plenty fast". Making that argument would require some serious benchmarking of the rust compiler though.
Off topic: I don't understand why the article repeatedly says the Rust compiler or procedural macros are already fast, even "plenty fast". Aren't they slower or about as slow as C++, which is notorious for being frustratingly slow, especially for local, non-distributed builds?
"Plenty fast" would mean fast enough for some simple use-case. e.g., "1G ethernet is plenty fast for a home network, but data centers can definitely benefit from faster links". i.e., fast enough you won't notice anything faster when doing your average user's daily activities, but certainly not fast enough for all users or activities.
The Rust compiler is then in no way "plenty fast". It has many benefits, and lots of hard work has gone into it, even optimizing it, but everyone would notice and benefit from it being any amount faster.