[HN Gopher] Why is the Rust compiler so slow?
       ___________________________________________________________________
        
       Why is the Rust compiler so slow?
        
       Author : Bogdanp
       Score  : 270 points
       Date   : 2025-06-26 19:29 UTC (1 days ago)
        
 (HTM) web link (sharnoff.io)
 (TXT) w3m dump (sharnoff.io)
        
       | ahartmetz wrote:
       | That person seems to be confused. Installing a single, statically
       | linked binary is clearly simpler than managing a container?!
        
         | hu3 wrote:
         | From the article, the goal was not to simplify, but rather to
         | modernize:
         | 
         | > So instead, I'd like to switch to deploying my website with
         | containers (be it Docker, Kubernetes, or otherwise), matching
         | the vast majority of software deployed any time in the last
         | decade.
         | 
         | Containers offer many benefits. To name some: process
         | isolation, increased security, standardized logging and mature
         | horizontal scalability.
        
           | adastra22 wrote:
           | So put the binary in the container. Why does it have to be
           | compiled within the container?
        
             | hu3 wrote:
             | That is what they are doing. It's a 2 stage Dockerfile.
             | 
             | First stage compiles the code. This is good for isolation
             | and reproducibility.
             | 
             | Second stage is a lightweight container to run the compiled
             | binary.
             | 
             | Why is the author being attacked (by multiple comments) for
             | not making things simpler when that was not claimed that as
             | the goal. They are modernizing it.
             | 
             | Containers are good practice for CI/CD anyway.
        
               | MobiusHorizons wrote:
               | That's a reasonable deployment strategy, but a pretty
               | terrible local development strategy
        
               | taberiand wrote:
               | Devcontainers are a good compromise though - you can
               | develop within a context that can be very nearly
               | identical to production; with a bit of finagling you
               | could even use the same dockerfile for the devcontainer,
               | and the build image and the deployed image
        
               | AndrewDucker wrote:
               | I'm not sure why "complicate things unnecessarily" is
               | considered more modern.
               | 
               | Don't do what you don't need to do.
        
               | hu3 wrote:
               | You realize the author is compiling a Rust webserver for
               | a static website right?
               | 
               | They are already long past the point of "complicate
               | things unnecessarily".
               | 
               | A simple Dockerfile pales in comparison.
        
               | adastra22 wrote:
               | Because he spends a good deal of the intro complaining
               | that this makes his dev practice slow. So don't do it! It
               | has nothing to do with docker but rather the fact he is
               | wiping the cache on every triggered build.
        
           | dwattttt wrote:
           | Mightily resisting the urge to be flippant, but all of those
           | benefits were achieved before Docker.
           | 
           | Docker is a (the, in some areas) modern way to do it, but far
           | from the only way.
        
           | a3w wrote:
           | Increased security compared to bare hardware, lower than VMs.
           | Also, lower than Jails and RKT (Rocket) which seems to be
           | dead.
        
           | eeZah7Ux wrote:
           | > process isolation, increased security
           | 
           | no, that's sandboxing.
        
         | jerf wrote:
         | Also strikes me as not fully understanding what exactly docker
         | is doing. In reference to building everything in a docker
         | image:
         | 
         | "Unfortunately, this will rebuild everything from scratch
         | whenever there's any change."
         | 
         | In this situation, with only one person as the builder, with no
         | need for CI or CD or whatever, there's nothing wrong with
         | building locally with all the local conveniences and just
         | slurping the result into a docker container. Double-check any
         | settings that may accidentally add paths if the paths have
         | anything that would bother you. (In my case it would merely
         | reveal that, yes, someone with my username built it and they
         | have a "src" directory... you can tell how worried I am about
         | both those tidbits by the fact I just posted them publicly.)
         | 
         | It's good for CI/CD in a professional setting to ensure that
         | you can build a project from a hard drive, a magnetic needle,
         | and a monkey trained to scratch a minimal kernel on to it, and
         | boot strap from there, but personal projects don't need that.
        
           | scuff3d wrote:
           | Thank you! I got a couple minutes in and was confused as
           | hell. There is no reason to do the builds in the container.
           | 
           | Even at work, I have a few projects where we had to build a
           | Java uber jar (all the dependencies bundled into one big far)
           | and when we need it containerized we just copy the jar in.
           | 
           | I honestly don't see much reason to do builds in the
           | container unless there is some limitation in my CICD pipeline
           | where I don't have access to necessary build tools.
        
             | mike_hearn wrote:
             | It's pretty clear that this whole project was god-tier
             | level procrastination so I wouldn't worry too much about
             | the details. The original stated problem could have been
             | solved with a 5-line shell script.
        
               | scuff3d wrote:
               | Not strictly related, but I got to the parts about using
               | a dependency to speed up builds in the container, and
               | that his website has "hundreds" of Rust dependencies, and
               | I was reminded why I get so annoyed with Rust. It's a
               | great language, but the practice of just duct taping a
               | bunch of dependencies together drives me nuts.
        
               | aaronblohowiak wrote:
               | what language are you using where you arent pulling in
               | deps for much of the work?
        
               | scuff3d wrote:
               | At work I prefer languages that take a "batteries
               | included" approach. So Go and Python are good examples.
               | You can get really far with just what is offered in the
               | standard libraries. Though obviously you can still pull
               | in a shitload of dependencies if you want.
               | 
               | In my own time I usually write C or Zig.
        
           | linkage wrote:
           | Half the point of containerization is to have reproducible
           | builds. You want a build environment that you can trust will
           | be identical 100% of the time. Your host machine is not that.
           | If you run `pacman -Syu`, you no longer have the same build
           | environment as you did earlier.
           | 
           | If you now copy your binary to the container and it
           | implicitly expects there to be a shared library in /usr/lib
           | or wherever, it could blow up at runtime because of a library
           | version mismatch.
        
             | missingdays wrote:
             | Nobody is suggesting to copy the binary to the Docker
             | container.
             | 
             | When developing locally, use `cargo test` in your cli. When
             | deploying to the server, build the Docker image on CI. If
             | it takes 5 minutes to build it, so be it.
        
         | vorgol wrote:
         | Exactly. I immediately thought of the grug brain dev when I
         | read that.
        
       | kelnos wrote:
       | > _This is... not ideal._
       | 
       | What? That's absolutely ideal! It's incredibly simple. I _wish_
       | deployment processes were always that simple! Docker is not going
       | to make your deployment process simpler than that.
       | 
       | I did enjoy the deep dive into figuring out what was taking a
       | long time when compiling.
        
         | quectophoton wrote:
         | One thing I like about Alpine Linux is how easy and dumbproof
         | it is to make packages. It's not some wild beast like trying to
         | create `.deb` files.
         | 
         | If anyone out there is already _fully_ committed to using only
         | Alpine Linux, I 'd recommend trying creating native packages at
         | least once.
        
           | eddd-ddde wrote:
           | I'm not familiar with .deb packages, but one thing I love
           | about Arch Linux is PKGBUILD and makepkg. It is ridiculously
           | easy to make a package.
        
       | tmtvl wrote:
       | Just set up a build server and have your docker containers fetch
       | prebuilt binaries from that?
        
       | adastra22 wrote:
       | As a former C++ developer, claims that rust compilation is slow
       | leave me scratching my head.
        
         | shadowgovt wrote:
         | I thorougly enjoy all the work on encapsulation and reducing
         | the steps of compilation to compile, then link that C does...
         | Only to have C++ come along and undo almost all of it through
         | the simple expedient of requiring templates for everything.
         | 
         | Oops, changed one template in one header. And that impacts....
         | 98% of my code.
        
         | eikenberry wrote:
         | Which is one of the reasons why Rust is considered to be
         | targeting C++'s developers. C++ devs already have the Stockholm
         | syndrome needed to tolerate the tooling.
        
           | MyOutfitIsVague wrote:
           | Rust's compilation is slow, but the tooling is just about the
           | best that any programming language has.
        
             | GuB-42 wrote:
             | How good is the debugger? "edit and continue"? Hot reload?
             | Full IDE?
             | 
             | I don't know enough Rust, but I find these aspects are
             | seriously lacking in C++ on Linux, and it is one of the few
             | things I think Windows has it better for developers. Is
             | Rust better?
        
               | adastra22 wrote:
               | No idea because I never do that. Nor does any rust
               | programmer I know. Which may answer your question ;)
        
               | frollogaston wrote:
               | "Rust devs don't use debuggers" isn't a good answer. The
               | one time I used Rust for some project like 7 years ago, I
               | did have to use a debugger, and it was fine.
        
               | mdaniel wrote:
               | > How good is the debugger? "edit and continue"?
               | 
               | Relevant: _Subsecond: A runtime hotpatching engine for
               | Rust hot-reloading_ -
               | https://news.ycombinator.com/item?id=44369642 - June,
               | 2024 (36 comments)
               | 
               | > Full IDE?
               | 
               | https://www.jetbrains.com/rust/ (newly free for non-
               | commercial use)
               | 
               | > find these aspects are seriously lacking in C++ on
               | Linux
               | 
               | https://www.jetbrains.com/clion/ (same, non-commercial)
        
               | steveklabnik wrote:
               | > debugger
               | 
               | I've only ever really used a debugger on embedded, we
               | used gdb there. I know VS: Code has a debugger that
               | works, I'm sure other IDEs do too.
               | 
               | > edit and continue
               | 
               | Hard to do in a pre-compiled language with no runtime, if
               | you're asking about what I think you're asking about.
               | 
               | > Hot reload
               | 
               | Other folks gave you good links, but this stuff is pretty
               | new, so I wouldn't claim that this is great and often
               | good and such.
               | 
               | > Full IDE
               | 
               | I'm not aware of Rust-specific IDEs, but many IDEs have
               | good support for Rust. VS: Code is the most popular
               | amongst users according to the annual survey. The Rust
               | Project distributes an official LSP server, so you can
               | use that with any editor that supports it.
        
               | izacus wrote:
               | So the answer is very clear "no" on all accounts, just
               | like for other languages built by people who don't
               | understand the value of good tooling.
        
               | frollogaston wrote:
               | I haven't used Rust much, but the tooling felt very
               | solid. There's a default package manager that works well,
               | unlike many other languages including C++ and somehow
               | Python. Debugging is fine. Idk why you expected edit-and-
               | continue, it's not like you get that in C++ either.
        
             | adastra22 wrote:
             | Slow compared to what? I'm still scraping my head at this.
             | My cargo builds are insanely fast, never taking more than a
             | minute or two even on large projects. The only ahead of
             | time compiled language I've used with faster compilation
             | speed is Go, and that is a language specifically designed
             | around (and arguably crippled by) the requirement for fast
             | compilation. Rust is comparable to C compilation, and
             | definitely faster than C++, Haskell, Java, Fortran, Algol,
             | and Common Lisp.
        
               | johnisgood wrote:
               | Just a few days ago I used cargo to install something. It
               | took like two minutes at the last stage. Definitely not
               | comparable to C or Fortran. I never had to wait that much
               | before. With C++? Definitely. Never with C though.
        
           | zozbot234 wrote:
           | > Stockholm syndrome
           | 
           | A.k.a. "Remember the Vasa!"
           | https://news.ycombinator.com/item?id=17172057
        
           | galangalalgol wrote:
           | Also modern c++ with value semantics is more functional than
           | many other languages people might come to rust from, that
           | keeps the borrow checker from being as annoying. If people
           | are used to making webs of stateful classes with references
           | to each pther. The borrow checker is horrific, but that is
           | because that design pattern is horrific if you multithread
           | it.
        
         | MobiusHorizons wrote:
         | Things can still be slow in absolute terms without being as
         | slow as C++. The issues with compiling C++ are incredibly well
         | understood and documented. It is one of the worst languages on
         | earth for compile times. Rust doesn't share those language
         | level issues, so the expectations are understandably higher.
        
           | int_19h wrote:
           | But it does share some of those issues. Specifically, while
           | Rust generics aren't as unstructured as C++ templates, the
           | main burden is actually from compiling all those tiny
           | instantiations, and Rust monomorphization has the same exact
           | problem responsible for the bulk of its compile times.
        
           | const_cast wrote:
           | Rust shares pretty much every language-level issue C++ has
           | with compile times, no? Monomorphization explosion, turing-
           | complete compile time macros, complex type system.
        
             | steveklabnik wrote:
             | There's a lot of overlap, but not that simple. Unless you
             | also discount C issues that C++ inherits. Even then,
             | there's subtleties and differences between the two that
             | matter.
        
         | oreally wrote:
         | Classic case of:
         | 
         | New features: yes
         | 
         | Talking to users and fixing actual problems: lolno, I CBF
        
       | ecshafer wrote:
       | The Rust compiler is slow. But if you want more features from
       | your compiler you need to have a slower compiler, there isn't a
       | way around that. However this blog post doesn't really seem to be
       | around that and more an annoyance in how they deploy binaries.
        
       | MangoToupe wrote:
       | I don't really consider it to be slow at all. It seems about as
       | performant as any other language this complexity, and it's far
       | faster than the 15 minute C++ and Scala build times I'd place in
       | the same category.
        
         | randomNumber7 wrote:
         | When C++ templates are turing complete is it pointless to
         | complain about the compile times without considering the actual
         | code :)
        
         | mountainriver wrote:
         | I also don't understand this, the rust compiler hardly bothers
         | me at all when I'm working. I feel like this is due to how bad
         | it was early on and people just sticking to that narrative
        
         | BanterTrouble wrote:
         | The memory usage is quite large compared to C/C++ when
         | compiling. I use Virtual Machines for Demos on my YouTube
         | Channel and compiling something large in Rust requires 8GB+.
         | 
         | In C/C++ I don't even have to worry about it.
        
           | windward wrote:
           | I can't say the same. Telling people to use `-j$(nproc)` in
           | lieu of `-j` to avoid the wrath of the OOM-killer is a rite
           | of passage
        
           | gpm wrote:
           | I can't agree, I've had C/C++ builds of well known open
           | source projects try to use >100GB of memory...
        
             | BanterTrouble wrote:
             | Maybe something else is going on then. I've done builds of
             | some large open source projects and most of the time they
             | was maxing the cores (I was building j32) but memory usage
             | was fine.
             | 
             | Out of interest what were they?
        
               | gpm wrote:
               | > Out of interest what were they?
               | 
               | The one that immediately comes to mind is cvc5... not
               | super recently though.
               | 
               | I suspect that "tried to" is doing a bit of work here.
               | The fact that it was failing and swapping out probably
               | meant that the more memory heavy g++ processes were going
               | slower than the memory light ones, resulting in more of
               | them running simultaneously than would likely have
               | happened in a normal successful build. Still, this was on
               | a system with 32GB of ram, so it was using roughly that
               | before swapping would slow down more memory intensive
               | processes.
        
       | AndyKelley wrote:
       | My homepage takes 73ms to rebuild: 17ms to recompile the static
       | site generator, then 56ms to run it.                   andy@bark
       | ~/d/andrewkelley.me (master)> zig build --watch -fincremental
       | Build Summary: 3/3 steps succeeded         install success
       | +- run exe compile success 57ms MaxRSS:3M            +- compile
       | exe compile Debug native success 331ms         Build Summary: 3/3
       | steps succeeded         install success         +- run exe
       | compile success 56ms MaxRSS:3M            +- compile exe compile
       | Debug native success 17ms         watching 75 directories, 1
       | processes
        
         | echelon wrote:
         | Zig is a small and simple language. It doesn't need a
         | complicated compiler.
         | 
         | Rust is a large and robust language meant for serious systems
         | programming. The scope of problems Rust addresses is large, and
         | Rust seeks to be deployed to very large scale software
         | problems.
         | 
         | These two are not the same and do not merit an apples to apples
         | comparison.
         | 
         | edit: I made some changes to my phrasing. I described Zig as a
         | "toy" language, which wasn't the right wording.
         | 
         | These languages are at different stages of maturity, have
         | different levels of complexity, and have different customers.
         | They shouldn't be measured against each other so superficially.
        
           | steveklabnik wrote:
           | Come on now. This isn't acceptable behavior.
           | 
           | (EDIT: The parent has since edited this comment to contain
           | more than just "zig bad rust good", but I still think the
           | combative-ness and insulting tone at the time I made this
           | comment isn't cool.)
        
             | echelon wrote:
             | > but I still think the combative-ness and insulting tone
             | at the time I made this comment isn't cool
             | 
             | Respectfully, the parent only offers up a Zig compile time
             | metric. That's it. That's the entire comment.
             | 
             | This HN post about Rust is now being dominated by a cheap
             | shot Zig one liner humblebrag from the lead author of Zig.
             | 
             | I think this thread needs a little more nuance.
        
               | steveklabnik wrote:
               | FWIW, I think your revised comment is far better, even
               | though I disagree with some of the framing, there's at
               | least some substance there.
               | 
               | Being frustrated by perceived bad behavior doesn't mean
               | responding with more bad behavior is a good way to
               | improve the discourse, if that's your goal here.
        
               | echelon wrote:
               | You're 100% right, Steve. Thank you for your voice of
               | moderation. You've been amazing to this community.
        
               | steveklabnik wrote:
               | It's all good. I'm very guilty of bad behavior myself a
               | lot of the time. It's on all of us to give gentle nudges
               | when we see each other getting out of line. I deserve to
               | be told the same if you see me doing this too!
        
               | Mawr wrote:
               | > Respectfully, the parent only offers up a Zig compile
               | time metric. That's it. That's the entire comment.
               | 
               | That's correct, but slinging cheap shots at each other is
               | not how discussions on this site are supposed to be.
               | 
               | > I think this thread needs a little more nuance.
               | 
               | Yes, but your comment offers none.
        
           | ummonk wrote:
           | This is an amusing argument to make in favor of Rust, since
           | it's exactly the kind of dismissive statement that Ada
           | proponents make about other languages including Rust.
        
         | taylorallred wrote:
         | @AndyKelley I'm super curious what you think the main factors
         | are that make languages like Zig super fast at compiling where
         | languages like Rust and Swift are quite slow. What's the key
         | difference?
        
           | AndyKelley wrote:
           | Basically, not depending on LLVM or LLD. The above is only
           | possible because we invested years into making our own x86_64
           | backend and our own linker. You can see all the people
           | ridiculing this decision 2 years ago
           | https://news.ycombinator.com/item?id=36529456
        
             | unclad5968 wrote:
             | LLVM isnt a good scapegoat. A C application equivalent in
             | size to a rust or c++ application will compile an order of
             | magnitude quicker and they all use LLVM. I'm not a compiler
             | expert, but it doesn't seem right to me that the only
             | possible path to quick compilation for Zig was a custom
             | backend.
        
               | MobiusHorizons wrote:
               | Be that as it may, many C compilers are still an order of
               | magnitude faster than LLVM. Probably the best example is
               | tcc, although it is not the only one. C is a much simpler
               | language than rust, so it is expected that compilation
               | should take less time for C. That doesn't mean llvm isn't
               | a significant contributor to compilation speed. I believe
               | cranelift compilation of rust is also much faster than
               | the llvm path
        
               | unclad5968 wrote:
               | > That doesn't mean llvm isn't a significant contributor
               | to compilation speed.
               | 
               | That's not what I said. I said it's unlikely that fast
               | compilation cannot be achieved while using LLVM which, I
               | would argue, is proven by the existence of a fast
               | compiler that uses LLVM.
        
               | int_19h wrote:
               | It will compile an order of magnitude quicker because it
               | often doesn't do the same thing - e.g. functions that are
               | aggressively inlined in C++ or Rust or Zig would be
               | compiled separately and linked normally, and generally
               | there's less equivalent of compile-time generics in C
               | code (because you have to either spell out all the
               | instantiations by hand or use preprocessor or a code
               | generator to do something that is two lines of code in
               | C++).
        
             | zozbot234 wrote:
             | The Rust folks have cranelift and wild BTW. There are
             | alternatives to LLVM and LLD, even though they might not be
             | as obvious to most users.
        
             | VeejayRampay wrote:
             | what is even the point of quoting reactions from two years
             | ago?
             | 
             | this is a terrible look for your whole community
        
               | elktown wrote:
               | Honestly I think it's good to highlight it. As a industry
               | we're too hampered by "Don't even try that, use the
               | existing thing" and it's causing these end results.
        
           | coolsunglasses wrote:
           | I'm also curious because I've (recently) compiled more or
           | less identical programs in Zig and Rust and they took the
           | same amount of time to compile. I'm guessing people are just
           | making Zig programs with less code and fewer dependencies and
           | not really comparing apples to apples.
        
             | kristoff_it wrote:
             | Zig is starting to migrate to custom backends for debug
             | builds (instead of using LLVM) plus incremental
             | compilation.
             | 
             | All Zig code is built in a single compilation unit and
             | everything is compiled from scratch every time you change
             | something, including all dependencies and all the parts of
             | the stdlib that you use in your project.
             | 
             | So you've been comparing Zig rebuilds that do all the work
             | every time with Rust rebuilds that cache all dependencies.
             | 
             | Once incremental is fully released you will see instant
             | rebuilds.
        
               | metaltyphoon wrote:
               | When does this land in Zig? Will aarch64 be supported?
        
               | mlugg wrote:
               | When targeting x86_64, the self-hosted backend is already
               | enabled by default on the latest builds of Zig (when
               | compiling in Debug mode). The self-hosted aarch64 backend
               | currently isn't generally usable (so we still default to
               | LLVM when targeting aarch64), but it's likely to be the
               | next ISA we focus on codegen for.
        
               | metaltyphoon wrote:
               | I assume x86_64 is Linux only correct?
        
               | AndyKelley wrote:
               | Not quite- any ELF or MachO target is enabled by default
               | already. Windows is waiting on some COFF linker bug
               | fixes.
        
           | steveklabnik wrote:
           | I'm not Andrew, but Rust has made several language design
           | decisions that make compiler performance difficult. Some
           | aspects of compiler speed come down to that.
           | 
           | One major difference is the way each project considers
           | compiler performance:
           | 
           | The Rust team has always cared to some degree about this.
           | But, from my recollection of many RFCs, "how does this impact
           | compiler performance" wasn't a first-class concern. And that
           | also doesn't really speak to a lot of the features that were
           | basically implemented before the RFC system existed. So while
           | it's important, it's secondary to other things. And so while
           | a bunch of hard-working people have put in a ton of work to
           | improve performance, they also run up against these more
           | fundamental limitations at the limit.
           | 
           | Andrew has pretty clearly made compiler performance a first-
           | class concern, and that's affected language design decisions.
           | Naturally this leads to a very performant compiler.
        
             | rtpg wrote:
             | > Rust has made several language design decisions that make
             | compiler performance difficult
             | 
             | Do you have a list off the top of your head/do you know of
             | a decent list? I've now read many "compiler slow"
             | thoughtpieces by many people and I have yet to see someone
             | point at a specific feature and say "this is just
             | intrinsically harder".
             | 
             | I believe that it likely exists, but would be good to know
             | what feature to get mad at! Half joking of course
        
               | Mawr wrote:
               | You can have your house built fast, cheap, or well. Pick
               | two; or a bit of all three that adds up to the same
               | effort required. You can't have all three.
               | 
               | You can't have a language with 100% of the possible
               | runtime perf, 100% of the possible compile speed and 100%
               | of the possible programmer ease-of-use.
               | 
               | At best you can abuse the law of diminishing returns aka
               | the 80-20 rule, but that's not easy to balance and you
               | run the risk of creating a language that's okay at
               | everything, but without any strong selling points, like
               | the stellar runtime performance Rust is known for.
               | 
               | So a better way to think about it is: Given Rust's
               | numerous benefits, is having subpar compilation time
               | really that big of a deal?
        
               | rtfeldman wrote:
               | > Given Rust's numerous benefits, is having subpar
               | compilation time really that big of a deal?
               | 
               | As someone who uses Rust as a daily driver at work at
               | zed.dev (about 600K LoC of Rust), and Zig outside of work
               | on roc-lang.org (which was about 300K LoC of Rust before
               | we decided to rewrite it in Zig, in significant part
               | because of Rust's compilation speed), yes - it is an
               | absolutely huge deal.
               | 
               | I like a lot of things about Rust, but its build times
               | are my biggest pain point.
        
               | mike_hearn wrote:
               | Rust heavily uses value types with specialized generics,
               | which explodes the work needed by the compiler. It can -
               | sometimes - improve performance. But it always slows down
               | compilation.
        
               | steveklabnik wrote:
               | Brian Anderson wrote up his thoughts here, and it's a
               | good intro to the topic:
               | https://www.pingcap.com/blog/rust-compilation-model-
               | calamity...
               | 
               | Let's dig into this bit of that, to give you some more
               | color:
               | 
               | > Split compiler/package manager -- although it is normal
               | for languages to have a package manager separate from the
               | compiler, in Rust at least this results in both cargo and
               | rustc having imperfect and redundant information about
               | the overall compilation pipeline. As more parts of the
               | pipeline are short-circuited for efficiency, more
               | metadata needs to be transferred between instances of the
               | compiler, mostly through the filesystem, which has
               | overhead.
               | 
               | > Per-compilation-unit code-generation -- rustc generates
               | machine code each time it compiles a crate, but it
               | doesn't need to -- with most Rust projects being
               | statically linked, the machine code isn't needed until
               | the final link step. There may be efficiencies to be
               | achieved by completely separating analysis and code
               | generation.
               | 
               | Rust decided to go with the classic separate compilation
               | model that languages like C use. Let's talk about that
               | compared to Zig, since it was already brought up in this
               | thread.
               | 
               | So imagine we have a project, A, and it depends on B. B
               | is a huge library, 200,000 lines of code, but we only use
               | one function from it in A, and that function is ten
               | lines. Yes, this is probably a bad project management
               | decision, but we're using extremes here to make a point.
               | 
               | Cargo will compile B first, and then A, and then link
               | things together. That's the classic model. And it works.
               | But it's slow: rust had to compile all 200,000 lines of
               | code in B, even though we only are gonna need ten lines.
               | We do all of this work, and then we throw it away at the
               | end. A ton of wasted time and effort. This is often
               | mitigated by the fact that you compile B once, and then
               | compile A a lot, but this still puts a lot of pressure on
               | the linker, and generics also makes this more complex,
               | but I'm getting a bit astray of the main point here, so
               | I'll leave that alone for now.
               | 
               | Zig, on the other hand, does not do this. It requires
               | that you compile your whole program all at once. This
               | means that they can drive the compilation process
               | beginning from main, in other words, only compile the
               | code that's actually reachable in your program. This
               | means that in the equivalent situation, Zig only compiles
               | those ten lines from B, and never bothers with the rest.
               | That's just always going to be faster.
               | 
               | Of course, there are pros and cons to both of these
               | decisions, Rust made the choice it did here for good
               | reasons. But it does mean it's just going to be slower.
        
           | AlienRobot wrote:
           | One difference that Zig has is that it doesn't have multiline
           | comments or multiline strings, meaning that the parser can
           | parse any line correctly without context. I assume this makes
           | parallelization trivial.
           | 
           | There is ino operator overloading like C, so A + B can only
           | mean one thing.
           | 
           | You can't redeclare a variable, so foo can only map to one
           | thing.
           | 
           | The list goes on.
           | 
           | Basically it was designed to compile faster, and that means
           | many issues on Github have been getting rejected in order to
           | keep it that way. It's full of compromises.
        
         | vlovich123 wrote:
         | Zig isn't memory safe though right?
        
           | pixelpoet wrote:
           | It isn't a lot of things, but I would argue that its
           | exceptionally (heh) good exception handling model /
           | philosophy (making it good, required, and performant) is more
           | important than memory safety, especially when a lot of
           | performance-oriented / bit-banging Rust code just gets shoved
           | into Unsafe blocks anyway. Even C/C++ can be made memory
           | safe, cf. https://github.com/pizlonator/llvm-project-deluge
           | 
           | What I'm more interested to know is what the runtime
           | performance tradeoff is like now; one really has to assume
           | that it's slower than LLVM-generated code, otherwise that
           | monumental achievement seems to have somehow been eclipsed in
           | very short time, with much shorter compile times to boot.
        
             | vlovich123 wrote:
             | > Even C/C++ can be made memory safe, cf.
             | https://github.com/pizlonator/llvm-project-deluge
             | 
             | > Fil-C achieves this using a combination of concurrent
             | garbage collection and invisible capabilities (each pointer
             | in memory has a corresponding capability, not visible to
             | the C address space)
             | 
             | With significant performance and memory overhead. That just
             | isn't the same ballpark that Rust is playing in although
             | hugely important if you want to bring forward performance
             | insensitive C code into a more secure execution
             | environment.
        
               | mike_hearn wrote:
               | Fil-C has advanced a lot since I last looked at it:
               | 
               |  _> Fil-C is currently 1.5x slower than normal C in good
               | cases, and about 4x slower in the worst cases._
               | 
               | with room for optimization still. Compatibility has
               | improved massively too, due to big changes to how it
               | works. The early versions were kind of toys, but if
               | Filip's claims about the current version hold up then
               | this is starting to look like a very useful bit of kit.
               | And he has the kind of background that means we should
               | take this seriously. There's a LOT of use cases for
               | taking stuff written in C and eliminating memory safety
               | issues for only a 50% slowdown.
        
             | jorvi wrote:
             | > especially when a lot of performance-oriented / bit-
             | banging Rust code just gets shoved into Unsafe blocks
             | anyway. Even C/C++ can be made memory safe, cf.
             | 
             | Your first claim is unverifiable and the second one is just
             | so, so wrong. Even big projects with very talented, well-
             | paid C or C++ devs eventually end up with CVEs, ~80% of
             | them memory-related. Humans are just not capable of 0%
             | error rate in their code.
             | 
             | If Zig somehow got more popular than C/C++, we would still
             | be stuck in the same CVE bog because of memory unsafety. No
             | thank you.
        
               | dgb23 wrote:
               | > If Zig somehow got more popular than C/C++, we would
               | still be stuck in the same CVE bog because of memory
               | unsafety. No thank you.
               | 
               | Zig does a lot of things to prevent or detect memory
               | safety related bugs. I personally haven't encountered a
               | single one so far, while learning the language.
               | 
               | > ~80% of them memory-related.
               | 
               | I assume you're referencing the 70% that MS has
               | published? I think they categorized null pointer
               | exceptions as memory safety bugs as well among other
               | things. Zig is strict about those, has error unions, and
               | is strict and explicit around casting. It can also detect
               | memory leaks and use after free among other things. It's
               | a language that's very explicit about a lot of things,
               | such as control flow, allocation strategies etc. And
               | there's comptime, which is a very potent tool to
               | guarantee all sorts of things that go well beyond memory
               | safety.
               | 
               | I almost want to say that your comment presents a false
               | dichotomy in terms of the safety concern, but I'm not an
               | expert in either Rust or Zig. I think however it's a bit
               | broad and unfair.
        
           | ummonk wrote:
           | Zig is less memory safe than Rust, but more than C/C++.
           | Neither Zig nor Rust is fundamentally memory safe.
        
             | Ar-Curunir wrote:
             | What? Zig is definitively not memory-safe, while safe Rust,
             | is, _by definition_ , memory-safe. Unsafe rust is not
             | memory-safe, but you generally don't need to have a lot of
             | it around.
        
               | ummonk wrote:
               | Safe Rust is demonstrably not memory-safe:
               | https://github.com/Speykious/cve-rs/tree/main
        
               | steveklabnik wrote:
               | This is a compiler bug. This has no bearing on the
               | language itself. Bugs happen, and they will be fixed,
               | even this one.
        
               | ummonk wrote:
               | It's a 10 year old bug which will eventually be fixed but
               | may require changes to how Rust handles type variance.
               | 
               | Until you guys write an actual formal specification, the
               | compiler is the language.
        
               | steveklabnik wrote:
               | It's a ten year old bug because it has never been found
               | in the wild, ever, in those ten years. Low impact, high
               | implementation effort bugs take less priority than bugs
               | that affect real users.
               | 
               | The project is adopting Ferrocene for the spec.
        
               | ummonk wrote:
               | Ferrocene is intended to document the behavior of the
               | current version of the rustc compiler, so it's just an
               | effort to formalize "the compiler is the language".
               | 
               | Yes, the soundness hole itself is low impact and doesn't
               | need to be prioritized but it undermines the binary "Zig
               | is definitively not memory-safe, while safe Rust, is, by
               | definition, memory-safe" argument that was made in
               | response to me. Now you're dealing with qualitative /
               | quantitative questions of practical impact, in which my
               | original statement holds: "Zig is less memory safe than
               | Rust, but more than C/C++. Neither Zig nor Rust is
               | fundamentally memory safe."
               | 
               | You can of course declare that Safe Rust is by definition
               | memory safe, but that doesn't make it any more true than
               | declaring that Rust solves the halting problem or that it
               | proves P=NP. RustBelt is proven sound. Rust by contrast,
               | as being documented by Ferrocene, is currently
               | fundamentally unsound (though you won't hit the soundness
               | issues in practice).
        
               | _flux wrote:
               | I believe these two statements should show the
               | fundamental difference:
               | 
               | - If a safe Rust program exhibits a memory safety
               | problem, it is a bug in the Rust compiler that is to be
               | fixed - If a Zig program exhibits a memory safety
               | problem, it is a bug in the Zig program that needs to be
               | fixed, not in the compiler
               | 
               | Wouldn't you agree?
               | 
               | > Ferrocene is intended to document the behavior of the
               | current version of the rustc compiler, so it's just an
               | effort to formalize "the compiler is the language".
               | 
               | I must admit I haven't read the specification, but I
               | doubt they attempt to be "bug for bug" compatible in the
               | sense that the spec enumerates memory safety bugs present
               | in the Rust compiler. But am I then mistaken?
        
               | ummonk wrote:
               | No, I don't agree. A compiler bug is something that gets
               | fixed in a patch release after it's reported, or perhaps
               | some platform-specific regression that gets fixed in the
               | next release after it's reported. What we're discussing
               | by contrast is a soundness hole in the language itself -
               | one which will most likely require breaking changes to
               | the language to close (i.e. some older programs that were
               | perfectly safe will fail to compile as a side effect of
               | tightening up the Rust language to prevent this soundness
               | hole).
               | 
               | As to the Ferrocene specification, it explicitly states
               | "Any difference between the FLS and the behavior of the
               | Rust compiler is considered an error on our part and the
               | FLS will be updated accordingly."
               | 
               | Proposals to fix the soundness hole in Rust either change
               | the variance rules themselves, or require where clauses
               | in certain places. Either of these changes would require
               | corresponding changes to chapter 4 of the Ferrocene
               | specification.
        
               | Ar-Curunir wrote:
               | And Rust has and will make those breaking changes, while
               | Zig will likely not. In fact there are documented and
               | blessed ways to break memory safety in Zig, and no one is
               | calling them soundness bugs!
               | 
               | I really don't see how you can claim with a straight face
               | that the two approaches are the same.
        
               | ummonk wrote:
               | "In fact there are documented and blessed ways to break
               | memory safety in Zig" - just as there are in Rust... even
               | the Rust standard library makes liberal use of them
               | (thereby making any program which invokes those parts of
               | the standard library transitively unsafe by definition).
               | 
               | Look, I'm not saying the Zig and Rust approaches are the
               | same. I explicitly stated that Rust is more memory safe
               | than Zig (which is in turn more memory safe than C/C++).
               | 
               | This is because Rust has clearly delineated a "safe"
               | subset of Rust which you have to explicitly opt out of
               | that is mostly sound (and has a goal of eventually being
               | entirely sound), has a culture of encouraging the use of
               | the safe subset, and has taken a good approach to the
               | interfacing of safe and unsafe code (i.e. if unsafe code
               | is properly written and satisfies the exposed contract -
               | despite the compiler being unable to verify this - then
               | safe code can safely be linked with it).
               | 
               | All of this results in extremely low risk of memory
               | corruption for Rust programs in practice (far lower than
               | any other commonly used non-GC language with the sole
               | exception of SPARK).
               | 
               | What you can't do though is reject the notion of memory
               | safety being a sliding scale and draw a binary
               | distinction between languages that are 100% perfectly
               | memory safe and languages that are memory unsafe. Well
               | you can, but Rust will fall on the side of memory unsafe
               | for many years to come. Java (ignoring vendor-specific
               | extensions) falls on the safe side though - the language
               | semantics as specified are sound _and_ it doesn 't even
               | have an unsafe subset.
        
               | steveklabnik wrote:
               | > As to the Ferrocene specification, it explicitly states
               | "Any difference between the FLS and the behavior of the
               | Rust compiler is considered an error on our part and the
               | FLS will be updated accordingly."
               | 
               | Right, this is from before it's adopted as the actual
               | spec, because it was from outside the project, and so
               | could not be.
               | 
               | Also, these goalposts are moving: it was "Rust doesn't
               | have a spec" and now it's "I don't like the spec."
               | 
               | Fixing this soundness hole does not require a breaking
               | change to the language. It is an implementation bug, not
               | a problem with the language as specified. But even if it
               | were, Rust's policies around soundness do allow for this,
               | and the project has done it in the past.
        
               | ummonk wrote:
               | The goalposts haven't moved. The goalposts were always
               | "the current compiler is the language".
               | 
               | If there is a proposed fix to the soundness hole that
               | wouldn't reject some existing sound Rust code, please
               | link to it; none of the proposed fixes I've seen do so.
               | And yes, Rust's policies do allow for breaking changes in
               | pursuit of soundness - likely some day many years from
               | now safe Rust will indeed be sound and guaranteed to be
               | memory safe.
        
               | vlovich123 wrote:
               | > If a safe Rust program exhibits a memory safety
               | problem, it is a bug in the Rust compiler that is to be
               | fixed - If a Zig program exhibits a memory safety
               | problem, it is a bug in the Zig program that needs to be
               | fixed, not in the compiler
               | 
               | That is the absolute best description of memory safety
               | I've heard expressed.
        
               | Graziano_M wrote:
               | The second you have any `unsafe`, Rust is _by definition_
               | not memory-safe.
        
               | Ar-Curunir wrote:
               | By that definition, Python is not memory-safe, Java is
               | not memory-safe, Go is not memory-safe, and so on. All of
               | these languages contain escape hatches to do memory-
               | unsafe stuff, yet no one is calling them memory unsafe.
        
               | ummonk wrote:
               | Go is more memory unsafe than Java or Rust. Data races in
               | concurrent Go code can cause memory corruption, unlike in
               | concurrent Java code. Safe Rust is designed to avoid data
               | races altogether using static analysis.
        
               | Meneth wrote:
               | And the majority of the Rust standard library uses
               | `unsafe`.
        
               | Measter wrote:
               | Prove it. Show me the stats that the standard library is
               | over 50% unsafe.
        
               | 9rx wrote:
               | "Over 50%" only holds if the statement is intended to be
               | binary. It may be that he considers direct use, no use,
               | and transitive use to be all different. In which case it
               | is possible that the majority[1] use unsafe, even if more
               | than 50% does not.
               | 
               | [1] The cult of the orange man would call this a
               | plurality, which may be what has tripped you up, but the
               | civilized world calls it a majority.
        
               | rurban wrote:
               | By definition yes. There were a lot of lies to persuade
               | managers. You can write a lot into your documentation.
               | 
               | But by implementation and spec definitely not.
        
           | kristoff_it wrote:
           | How confident are you that memory safety (or lack thereof) is
           | a significant variable in how fast a compiler is?
        
         | whoisyc wrote:
         | Just like every submission about C/C++ gets a comment about how
         | great Rust is, every submission about Rust gets a comment about
         | how great Zig is. Like a clockwork.
         | 
         | Edit: apparently I am replying to the main Zig author? Language
         | evangelism is by far the worst part of Rust and has likely
         | stirred up more anti Rust sentiment than "converting" people to
         | Rust. If you truly care for your language you should use
         | whatever leverage you have to steer your community away from
         | evangelism, not embrace it.
        
           | AlienRobot wrote:
           | If you can't be proud about a programming language you made
           | what is even the point?
        
           | frollogaston wrote:
           | It's unlikely that anyone was going to use Rust anyway but
           | decided not to because they got too annoyed hearing about it.
        
         | qualeed wrote:
         | Neat, I guess?
         | 
         | This comment would be a lot better if it engaged with the
         | posted article, or really had any sort of insight beyond a
         | single compile time metric. What do you want me to take away
         | from your comment? Zig good and Rust bad?
        
           | kristoff_it wrote:
           | I think the most relevant thing is that building a simple
           | website can (and should) take milliseconds, not minutes, and
           | that -- quoting from the post:
           | 
           | > A brief note: 50 seconds is fine, actually!
           | 
           | 50 seconds should actually not be considered fine.
        
             | qualeed wrote:
             | As you've just demonstrated, that point can be made without
             | even mentioning Zig, let alone copy/pasting some compile
             | time stuff with no other comment or context. Which is why I
             | thought (well, hoped) there might be something more to it
             | than just a dunk attempt.
             | 
             | Now we get all of this off-topic discussion about Zig.
             | Which I guess is good for you Zig folk... But it's pretty
             | off-putting for me.
             | 
             | whoisyc's comment is extremely on point. As the VP of
             | community, I would really encourage thinking about what
             | they said.
        
               | kristoff_it wrote:
               | > As you've just demonstrated, that point can be made
               | without even mentioning Zig, let alone copy/pasting some
               | compile time stuff with no other comment or context.
               | Which is why I thought (well, hoped) there might be
               | something more to it than just a dunk attempt.
               | 
               | Having concrete proof that something can be done more
               | efficiently is extremely important and, no, I haven't
               | "demonstrated" anything, since my earlier comment would
               | have had way less substance to it without the previous
               | context.
               | 
               | The comment from Andrew is not just random compiler
               | stats, but a datapoint showing a comparable example
               | having dramatically different performance
               | characteristics.
               | 
               | You can find in this very HN submission various comments
               | that assume that Rust's compiler performance is
               | impossible to improve because of reasons that actually
               | are mostly (if not entirely) irrelevant. Case in point,
               | see people talking about how Rust compilation must take
               | longer because of the borrow checker (and other safety
               | checks) and Steve pointing out that, no, actually that
               | part of the compilation pipeline is very small.
               | 
               | > Now we get all of this off-topic discussion about Zig.
               | 
               | So no, I would argue the opposite: this discussion is
               | very much on topic.
        
               | maccard wrote:
               | I disagree. Zig and go are perfect frames of reference to
               | say "actually no, Rust really is slow. Here are examples
               | for you to go and see for yourself"
        
         | ww520 wrote:
         | Nice. Didn't realize zig build has --watch and -fincremental
         | added. I was mostly using "watchexec -e zig zig build" for
         | recompile on file changes.
        
           | Graziano_M wrote:
           | New to 0.14.0!
        
         | nicoburns wrote:
         | My non-static Rust website (includes an actual webserver as
         | well as a react-like framework for templating) takes 1.25s to
         | do an incremental recompile with "cargo watch" (which is an
         | external watcher that just kills the process and reruns "cargo
         | run").
         | 
         | And it can be considerably faster if you use something like
         | subsecond[0] (which does incremental linking and hotpatches the
         | running binary). It's not quite as fast as Zig, but it's close.
         | 
         | However, if that 331ms build above is a clean (uncached) build
         | then that's a _lot_ faster than a clean build of my website
         | which takes ~12s.
         | 
         | [0]: https://news.ycombinator.com/item?id=44369642
        
           | AndyKelley wrote:
           | The 331ms time is mostly uncached. In this case the build
           | script was already cached (must be re-done if the build
           | script is edited), and compiler_rt was already cached (must
           | be done exactly once per target; almost never rebuilt).
        
             | nicoburns wrote:
             | Impressive!
        
       | namibj wrote:
       | Incremental compilation good. If you want, freeze the initial
       | incremental cache after a single fresh build to use for
       | building/deploying updates, to mitigate the risk of intermediate
       | states gradually corrupting the cache.
       | 
       | Works great with docker: upon new compiler version or major
       | website update, rebuild the layer with the incremental cache;
       | otherwise just run from the snapshot and build newest website
       | update version/state, and upload/deploy the resulting static
       | binary. Just set so that mere code changes won't force rebuilding
       | the layer that caches/materializes the fresh clean build's
       | incremental compilation cache.
        
         | maccard wrote:
         | The intermediates for my project are 150GB+ alone. Last time I
         | worked with docker images that large we had massive massive
         | problems.
        
       | b0a04gl wrote:
       | rust prioritises build-time correctness: no runtime linker or no
       | dynamic deps. all checks (types, traits, ownership) happen before
       | execution. this makes builds sensitive to upstream changes.
       | docker uses content-hash layers, so small context edits
       | invalidate caches. without careful layer ordering, rust gets
       | fully recompiled on every change.
        
       | RS-232 wrote:
       | Is there an equivalent of ninja for rust yet?
        
         | steveklabnik wrote:
         | It depends on what you mean by 'equivalent of ninja.'
         | 
         | Cargo is the standard build system for Rust projects, though
         | some users use other ones. (And some build those on top of
         | Cargo too.)
        
       | senderista wrote:
       | WRT compilation efficiency, the C/C++ model of compiling separate
       | translation units in parallel seems like an advance over the Rust
       | model (but obviously forecloses opportunities for whole-program
       | optimization).
        
         | woodruffw wrote:
         | Rust can and does compile separate translation units in
         | parallel; it's just that the translation unit is (roughly) a
         | crate instead of a single C or C++ source file.
        
           | EnPissant wrote:
           | And even for crates, Rust has incremental compilation.
        
       | ic_fly2 wrote:
       | This is such a weird cannon on sparrows approach.
       | 
       | The local builds are fast, why would you rebuild docker for small
       | changes?
       | 
       | Also why is a personal page so much rust and so many
       | dependencies. For a larger project with more complex stuff you'd
       | have a test suite that takes time too. Run both in parallel in
       | your CI and call it a day.
        
       | taylorallred wrote:
       | So there's this guy you may have heard of called Ryan Fleury who
       | makes the RAD debugger for Epic. The whole thing is made with
       | 278k lines of C and is built as a unity build (all the code is
       | included into one file that is compiled as a single translation
       | unit). On a decent windows machine it takes 1.5 seconds to do a
       | clean compile. This seems like a clear case-study that
       | compilation _can_ be incredibly fast and makes me wonder why
       | other languages like Rust and Swift can 't just do something
       | similar to achieve similar speeds.
        
         | js2 wrote:
         | "Just". Probably because there's a lot of complexity you're
         | waving away. Almost nothing is ever simple as "just".
        
           | pixelpoet wrote:
           | At a previous company, we had a rule: whoever says "just"
           | gets to implement it :)
        
             | forrestthewoods wrote:
             | I wanted to ban "just" but your rule is better. Brilliant.
        
           | taylorallred wrote:
           | That "just" was too flippant. My bad. What I meant to convey
           | is "hey, there's some fast compiling going on here and it
           | wasn't that hard to pull off. Can we at least take a look at
           | why that is and maybe do the same thing?".
        
             | steveklabnik wrote:
             | > "hey, there's some fast compiling going on here and it
             | wasn't that hard to pull off. Can we at least take a look
             | at why that is and maybe do the same thing?".
             | 
             | Do you really believe that nobody over the course of Rust's
             | lifetime has ever taken a look at C compilers and thought
             | about if techniques they use could apply to the Rust
             | compiler?
        
               | taylorallred wrote:
               | Of course not. But it wouldn't surprise me if nobody
               | thought to use a unity build. (Maybe they did. Idk. I'm
               | curious).
        
               | steveklabnik wrote:
               | Rust and C have differences around compilation units:
               | Rust's already tend to be much larger than C on average,
               | because the entire crate (aka tree of modules) is the
               | compilation unit in Rust, as opposed to the file-based
               | (okay not if you're on some weird architecture)
               | compilation unit of C.
               | 
               | Unity builds are useful for C programs because they tend
               | to reduce header processing overhead, whereas Rust does
               | not have the preprocessor or header files at all.
               | 
               | They also can help with reducing the number of object
               | files (down to one from many), so that the linker has
               | less work to do, this is already sort of done (though not
               | to literally one) due to what I mentioned above.
               | 
               | In general, the conventional advice is to do the exact
               | opposite: breaking large Rust projects into more, smaller
               | compilation units can help do less "spurious" rebuilding,
               | so smaller changes have less overall impact.
               | 
               | Basically, Rust's compile time issues lie elsewhere.
        
               | ameliaquining wrote:
               | Can you explain why a unity build would help?
               | Conventional wisdom is that Rust compilation is slow in
               | part because it has too _few_ translation units (one per
               | crate, plus codegen units which only sometimes work), not
               | too many.
        
         | tptacek wrote:
         | I don't think it's interesting to observe that C code can be
         | compiled quickly (so can Go, a language designed specifically
         | for fast compilation). It's not a problem intrinsic to
         | compilation; the interesting hard problem is to make _Rust 's_
         | semantics compile quickly. This is a FAQ on the Rust website.
        
         | lordofgibbons wrote:
         | The more your compiler does for you at build time, the longer
         | it will take to build, it's that simple.
         | 
         | Go has sub-second build times even on massive code-bases. Why?
         | because it doesn't do a lot at build time. It has a simple
         | module system, (relatively) simple type system, and leaves a
         | whole bunch of stuff be handled by the GC at runtime. It's
         | great for its intended use case.
         | 
         | When you have things like macros, advanced type systems, and
         | want robustness guarantees at build time.. then you have to pay
         | for that.
        
           | ChadNauseam wrote:
           | That the type system is responsible for rust's slow builds is
           | a common and enduring myth. `cargo check` (which just does
           | typechecking) is actually usually pretty fast. Most of the
           | build time is spent in the code generation phase. Some macros
           | do cause problems as you mention, since the code that
           | contains the macro must be compiled before the code that uses
           | it, so they reduce parallelism.
        
             | rstuart4133 wrote:
             | > Most of the build time is spent in the code generation
             | phase.
             | 
             | I can believe that, but even so it's caused by the type
             | system monomorphising everything. When it use qsort from
             | libc, you are using per-compiled code from a library. When
             | you use slice::sort(), you get custom assembler compiled to
             | suit your application. Thus, there is a _lot_ more code
             | generation going on, and that is caused by the tradeoffs
             | they 've made with the type system.
             | 
             | Rusts approach give you all sorts of advantages, like fast
             | code and strong compile time type checking. But it comes
             | with warts too, like fat binaries, and a bug in
             | slice::sort() can't be fixed by just shipping of the std
             | dynamic library, because there is no such library. It's
             | been recompiled, just for you.
             | 
             | FWIW, modern C++ (like boost) that places everything in
             | templates in .h files suffers from the same problem. If
             | Swift suffers from it too, I'd wager it's the same cause.
        
               | badmintonbaseba wrote:
               | It's partly by the type system. You can implement a
               | std::sort (or slice::sort()) that just delegates to qsort
               | or a qsort-like implementation and have roughly the same
               | compile time performance as just using qsort straight.
               | 
               | But not having to is a win, as the monomorphised sorts
               | are just much faster at runtime than having to do an
               | indirect call for each comparison.
        
               | estebank wrote:
               | This is a pattern a crate author can rely on (write a
               | function that uses genetics that immediately delegates to
               | a function that uses trait objects or converts to the
               | needed types eagerly so the common logic gets compiled
               | only once), and there have been multiple efforts to have
               | the compiler do that automatically. It has been called
               | polymorphization and it comes up every now and then:
               | https://internals.rust-lang.org/t/add-back-
               | polymorphization/...
        
             | tedunangst wrote:
             | I just ran cargo check on nushell, and it took a minute and
             | a half. I didn't time how long it took to compile, maybe
             | five minutes earlier today? So I would call it faster, but
             | still not fast.
             | 
             | I was all excited to conduct the "cargo check; mrustc; cc"
             | is 100x faster experiment, but I think at best, the
             | multiple is going to be pretty small.
        
               | ChadNauseam wrote:
               | Did you do it from a clean build? In that case, it's
               | actually a slightly misleading metric, since rust needs
               | to actually compile macros in order to typecheck code
               | that uses them. (And therefore must also compile all the
               | code that the macro depends on.) My bad for suggesting
               | it, haha. Incremental cargo check is often a better way
               | of seeing how long typechecking takes, since usually you
               | haven't modified any macros that will need to be
               | recompiled. On my project at work, incremental cargo
               | check takes `1.71s`.
        
               | estebank wrote:
               | Side note: There's an effort to cache proc macro
               | invocations so that they get _executed_ only once if the
               | item they annotate hasn 't changed:
               | https://github.com/rust-lang/rust/pull/129102
               | 
               | There are multiple caveats on providing this to users (we
               | can't _assume_ that macro invocations are idempotent, so
               | the new behavior would have to be opt in, and this only
               | benefits incremental compilation), but it 's in our
               | radar.
        
               | CryZe wrote:
               | A ton of that is actually still doing codegen (for the
               | proc macros for example).
        
           | duped wrote:
           | I think this is mostly a myth. If you look at Rust compiler
           | benchmarks, while typechecking isn't _free_ it's also not the
           | bottleneck.
           | 
           | A big reason that amalgamation builds of C and C++ can
           | absolutely fly is because they aren't reparsing headers and
           | generating exactly one object file so the _linker_ has no
           | work to do.
           | 
           | Once you add static linking to the toolchain (in all of its
           | forms) things get really fucking slow.
           | 
           | Codegen is also a problem. Rust tends to generate a lot more
           | code than C or C++, so while the compiler is done doing most
           | of its typechecking work, the backend and assembler has a lot
           | of things to chuck through.
        
             | treyd wrote:
             | Not only does it generate more code, the initially
             | generated code before optimizations is also often worse.
             | For example, heavy use of iterators means a ton of generics
             | being instantiated and a ton of call code for setting up
             | and tearing down call frames. This gets heavily inlined and
             | flattened out, so in the end it's extremely well-optimized,
             | but it's a lot of work for the compiler. Writing it all out
             | classically with for loops and ifs is possible, but it's
             | harder to read.
        
               | estebank wrote:
               | For loops are sugar around an Iterator instantiation:
               | for i in 0..10 {}
               | 
               | translates to roughly                 let mut iter =
               | Range { start: 0, end: 10 }.into_iter();       while let
               | Some(i) = iter.next() {}
        
             | fingerlocks wrote:
             | The swift compiler is definitely bottle necked by type
             | checking. For example, as a language requirement, generic
             | types are left more or less in-tact after compilation. They
             | are type checked independent of what is happening. This is
             | unlike C++ templates which are effectively copy-pasting the
             | resolved type with the generic for every occurrence of type
             | resolution.
             | 
             | This has tradeoffs: increased ABI stability at the cost of
             | longer compile times.
        
               | willtemperley wrote:
               | A lot can be done by the programmer to mitigate slow
               | builds in Swift. Breaking up long expressions into
               | smaller ones and using explicit types where type
               | inference is expensive for example.
               | 
               | I'd like to see tooling for this to pinpoint bottlenecks
               | - it's not always obvious what's making builds slow.
        
               | ykonstant wrote:
               | >I'd like to see tooling for this to pinpoint bottlenecks
               | - it's not always obvious what's making builds slow.
               | 
               | I second this enthusiastically.
        
               | glhaynes wrote:
               | I'll third it. I've started to see more and more cargo
               | culting of "fixes" that I'm extremely suspicious do
               | nothing aside from making the code bulkier.
        
               | never_inline wrote:
               | > Breaking up long expressions into smaller ones
               | 
               | If it improves compile time, that sounds like a bug in
               | the compiler or the design of the language itself.
        
               | windward wrote:
               | >This is unlike C++ templates which are effectively copy-
               | pasting the resolved type with the generic for every
               | occurrence of type resolution.
               | 
               | Even this can lead to unworkable compile times, to the
               | point that code is rewritten.
        
               | slavapestov wrote:
               | > This has tradeoffs: increased ABI stability at the cost
               | of longer compile times.
               | 
               | Nah. Slow type checking in Swift is primarily caused by
               | the fact that functions and operators can be overloaded
               | on type.
               | 
               | Separately-compiled generics don't introduce any
               | algorithmic complexity and are actually good for compile
               | time, because you don't have to re-type check every
               | template expansion more than once.
        
               | fingerlocks wrote:
               | You're absolutely right. I realized this later but it was
               | too late to edit the post.
        
               | choeger wrote:
               | Separate compilation also enables easy parallelization of
               | type checking.
        
             | windward wrote:
             | >Codegen is also a problem. Rust tends to generate a lot
             | more code than C or C++
             | 
             | Wouldn't you say a lot of that comes from the macros and
             | (by way of monomorphisation) the type system?
        
               | jandrewrogers wrote:
               | Modern C++ in particular does a lot of similar, albeit
               | not identical, codegen due to its extensive
               | metaprogramming facilities. (C is, of course, dead
               | simple.) I've never looked into it too much but
               | anecdotally Rust does seem to generate significantly more
               | code than C++ in cases where I would intuitively expect
               | the codegen to be similar. For whatever reason, the "in
               | theory" doesn't translate to "in practice" reliably.
               | 
               | I suspect this leaks into both compile-time and run-time
               | costs.
        
             | the-lazy-guy wrote:
             | > Once you add static linking to the toolchain (in all of
             | its forms) things get really fucking slow.
             | 
             | Could you expand on that, please? Every time you run
             | dynmically linked program, it is linked at runtime. (unless
             | it explicitly avoids linking unneccessary stuff by
             | dlopening things lazily; which pretty much never happens).
             | If it is fine to link on every program launch, linking at
             | build time should not be a problem at all.
             | 
             | If you want to have link time optimization, that's another
             | story. But you absolutely don't have to do that if you care
             | about build speed.
        
             | blizdiddy wrote:
             | Go is static by default and still fast as hell
        
               | vintagedave wrote:
               | Delphi is static by default and incredibly fast too.
        
               | zenlot wrote:
               | FreePascal to the game please
        
             | benreesman wrote:
             | The meme that static linking is slow or produces anything
             | other than the best executables is demonstrably false and
             | the result of surprisingly sinister agendas. Get out
             | readelf and nm and PS sometime and do the arithematic: most
             | programs don't link much of glibc (and its static link is
             | broken by design, musl is better at just about everything).
             | Matt Godbolt has a great talk about how dynamic linking
             | actually works that should give anyone pause.
             | 
             | DLLs got their start when early windowing systems didn't
             | quite fit on the workstations of the era in the late 80s /
             | early 90s.
             | 
             | In about 4 minutes both Microsoft and GNU were like, "let
             | me get this straight, it will never work on another system
             | and I can silently change it whenever I want?" Debian went
             | along because it gives distro maintainers degrees of
             | freedom they like and don't bear the costs of.
             | 
             | Fast forward 30 years and Docker is too profitable a
             | problem to fix by the simple expedient of calling a stable
             | kernel ABI on anything, and don't even get me started on
             | how penetrated everything but libressl and libsodium are.
             | Protip: TLS is popular with the establishment because even
             | Wireshark requires special settings and privileges for a
             | user to _see their own traffic_ , security patches my ass.
             | eBPF is easier.
             | 
             | Dynamic linking moves control from users to vendors and
             | governments at ruinous cost in performance, props up
             | bloated industries like the cloud compute and Docker
             | industrial complex, and should die in a fire.
             | 
             | Don't take my word for it, swing by cat-v.org sometimes and
             | see what the authors of Unix have to say about it.
             | 
             | I'll save the rant about how rustc somehow manages to be
             | slower than clang++ and clang-tidy combined for another
             | day.
        
               | jelder wrote:
               | CppCon 2018: Matt Godbolt "The Bits Between the Bits: How
               | We Get to main()"
               | 
               | https://www.youtube.com/watch?v=dOfucXtyEsU
        
               | jrmg wrote:
               | _...surprisingly sinister agendas._
               | 
               | ...
               | 
               |  _Dynamic linking moves control from users to vendors and
               | governments at ruinous cost in performance, props up
               | bloated industries..._
               | 
               | This is ridiculous. Not everything is a conspiracy!
        
               | k__ wrote:
               | That's an even more reasonable fear than trusting trust,
               | and people seem to take that seriously.
        
               | benreesman wrote:
               | I didn't say anything was a conspiracy, let alone
               | everything. I said inferior software is promoted by
               | vendors _on Linux as well as on MacOS and Windows_ with
               | unpleasant consequences for users in a way that serves
               | those vendors and the even more powerful institutions to
               | which they are beholden. Sinister intentions are
               | everywhere in this business (go read the opinions of the
               | people who run YC), that 's not even remotely
               | controversial.
               | 
               | If fact, if there was anything remotely controversial
               | about a bunch of extremely specific, extremely
               | falsifiable claims I made, one imagines your rebuttal
               | would have mentioned at least one.
               | 
               | I said inflmatory things (Docker is both arsonist and
               | fireman at ruinous cost), but they're fucking true. That
               | Alpine in the Docker jank? Links musl!
        
               | computably wrote:
               | Bad incentives != conspiracy
        
               | trinix912 wrote:
               | Had they left "governments" out of there it would've been
               | almost fine, but damn I didn't know it's now governments
               | changing DLLs for us!
        
               | benreesman wrote:
               | https://en.wikipedia.org/wiki/Equation_Group
               | 
               | https://en.wikipedia.org/wiki/Advanced_persistent_threat
               | 
               | https://en.wikipedia.org/wiki/Operation_Olympic_Games
               | 
               | https://simple.wikipedia.org/wiki/Stuxnet
               | 
               | https://en.wikipedia.org/wiki/Cozy_Bear
               | 
               | https://en.wikipedia.org/wiki/Fancy_Bear
               | 
               | https://en.wikipedia.org/wiki/Tailored_Access_Operations
        
               | duped wrote:
               | I think you're confused about my comment and this thread
               | - I'm talking about _build_ times.
        
               | benreesman wrote:
               | You said something false and important and I took the
               | opportunity to educate anyone reading about why this
               | aspect of their computing experience is a mess. All of
               | that is germane to how we ended up in a situation where
               | someone is calling rustc with a Dockerfile and this is
               | considered normal.
        
               | duped wrote:
               | Seems like you still misunderstand both the comment and
               | context and getting overly emotional/conspiratorial. You
               | might want to work on those feelings.
        
               | benreesman wrote:
               | No one is trying to take anyone's multi-gigabyte pile of
               | dynamic library closure to deploy what should be a few
               | hundred kilobytes of arbitrarily portable, secure by
               | construction, built to last executable.
               | 
               | But people should make an informed choice, and there
               | isn't any noble or high minded or well-meaning reason to
               | try to shout that information down.
               | 
               | Don't confidently assert falsehoods unless you're
               | prepared to have them refuted. You're entitled to peddle
               | memes and I'm entitled to reply with corrections.
        
           | cogman10 wrote:
           | Yes but I'd also add that Go specifically does not optimize
           | well.
           | 
           | The compiler is optimized for compilation speed, not runtime
           | performance. Generally speaking, it does well enough.
           | Especially because it's usecase is often applications where
           | "good enough" is good enough (IE, IO heavy applications).
           | 
           | You can see that with "gccgo". Slower to compile, faster to
           | run.
        
             | cherryteastain wrote:
             | Is gccgo really faster? Last time I looked it looked like
             | it was abandoned (stuck at go 1.18, had no generics
             | support) and was not really faster than the "actual"
             | compiler.
        
               | cogman10 wrote:
               | Digging around, looks like it's workload dependent.
               | 
               | For pure computational workloads, it'll be faster.
               | However, anything with heavy allocation will suffer as
               | apparently the gccgo GC and GC related optimizations
               | aren't as good as cgo's.
        
             | pclmulqdq wrote:
             | Go defaults to an unoptimized build. If you want it to run
             | heavy optimization passes, you can turn those on with
             | flags. Rust defaults to doing most of those optimizations
             | on every build and allows you to turn them off.
        
           | Zardoz84 wrote:
           | Dlang compilers does more than any C++ compiler
           | (metaprogramming, a better template system and compile time
           | execution) and it's hugely faster. Language syntax design has
           | a role here.
        
           | Mawr wrote:
           | Not really. The root reason behind Go's fast compilation is
           | that it was specifically designed to compile fast. The
           | implementation details are just a natural consequence of that
           | design decision.
           | 
           | Since fast compilation was a goal, every part of the design
           | was looked at through a rough "can this be a horrible
           | bottleneck?", and discarded if so. For example, the import
           | (package) system was designed to avoid the horrible,
           | inefficient mess of C++. It's obvious that you never want to
           | compile the same package more than once and that you need to
           | support parallel package compilation. These may be blindingly
           | obvious, but if you don't think about compilation speed at
           | design time, you'll get this wrong and will never be able to
           | fix it.
           | 
           | As far as optimizations vs compile speed goes, it's just a
           | simple case of diminishing returns. Since Rust has maximum
           | possible perfomance as a goal, it's forced to go well into
           | the diminishing returns territory, sacrificing a ton of
           | compile speed for minor performance improvements. Go has far
           | more modest performance goals, so it can get 80% of the
           | possible performance for only 20% of the compile cost. Rust
           | can't afford to relax its stance because it's competing with
           | languages like C++, and to some extent C, that are willing to
           | go to any length to squeeze out an extra 1% of perfomance.
        
           | jstanley wrote:
           | > Go has sub-second build times even on massive code-bases.
           | 
           | Unless you use sqlite, in which case your build takes a
           | million years.
        
             | Groxx wrote:
             | Yeah, I deal with multiple Go projects that take a couple
             | minutes to link the final binary, much less build all the
             | intermediates.
             | 
             | Compilation speed depends on what you do with a language.
             | "Fast" is not an absolute, and for most people it depends
             | heavily on community habits. Rust habits tend to favor
             | extreme optimizability and/or extreme compile-time
             | guarantees, and that's obviously going to be slower than
             | simpler code.
        
             | infogulch wrote:
             | Try https://github.com/ncruces/go-sqlite3 it runs sqlite in
             | WASM with wazero, a pure Go WASM runtime, so it builds
             | without any CGo required. Most of the benchmarks are within
             | a few % of the performance of mattn/go-sqlite3.
        
           | phplovesong wrote:
           | Thats not really true. As a counter example, Ocaml has a very
           | advanced type system, full typeinference, generics and all
           | that jazz. Still its on par, or even faster to compile than
           | Go.
        
         | dhosek wrote:
         | Because Russt and Swift are doing much more work than a C
         | compiler would? The analysis necessary for the borrow checker
         | is not free, likewise with a lot of other compile-time checks
         | in both languages. C can be fast because it effectively does no
         | compile-time checking of things beyond basic syntax so you can
         | call foo(char _) with foo(int_ ) and other unholy things.
        
           | drivebyhooting wrote:
           | That's not a good example. Foo(int) is analyzed by compiler
           | and a type conversion is inserted. The language spec might be
           | bad, but this isn't letting the compiler cut corners.
        
           | steveklabnik wrote:
           | The borrow checker is usually a blip on the overall graph of
           | compilation time.
           | 
           | The overall principle is sound though: it's true that doing
           | some work is more than doing no work. But the borrow checker
           | and other safety checks are not the root of compile time
           | performance in Rust.
        
             | kimixa wrote:
             | While the borrow checker is one big difference, it's
             | certainly not the _only_ thing the rust compiler offers on
             | top of C that takes more work.
             | 
             | Stuff like inserting bounds checking puts more work on the
             | optimization passes and codegen backend as it simply has to
             | deal with more instructions. And that then puts more
             | symbols and larger sections in the input to the linker,
             | slowing that down. Even if the frontend "proves" it's
             | unnecessary that calculation isn't free. Many of those
             | features are related to "safety" due to the goals of the
             | language. I doubt the syntax itself really makes much of a
             | difference as the parser isn't normally high on the
             | profiled times either.
             | 
             | Generally it provides stricter checks that are normally
             | punted to a linter tool in the c/c++ world - and nobody has
             | accused clang-tidy of being _fast_ :P
        
               | simonask wrote:
               | It truly is not about bounds checks. Index lookups are
               | rare in practical Rust code, and the amount of code
               | generated from them is miniscule.
               | 
               | But it _is_ about the sheer volume of stuff passed to
               | LLVM, as you say, which comes from a couple of places,
               | mostly related to monomorphization (generics), but also
               | many calls to tiny inlined functions. Incidentally, this
               | is also what makes many "modern" C++ projects slow to
               | compile.
               | 
               | In my experience, similarly sized Rust and C++ projects
               | seem to see similar compilation times. Sometimes C++ wins
               | due to better parallelization (translation units in Rust
               | are crates, not source files).
        
           | taylorallred wrote:
           | These languages do more at compile time, yes. However, I
           | learned from Ryan's discord server that he did a unity build
           | in a C++ codebase and got similar results (just a few seconds
           | slower than the C code). Also, you could see in the article
           | that most of the time was being spent in LLVM and linking.
           | With a unity build, you nearly cut out link step entirely.
           | Rust and Swift do some sophisticated things (hinley-milner,
           | generics, etc.) but I have my doubts that those things cause
           | the most slowdown.
        
           | Thiez wrote:
           | This explanation gets repeated over and over again in
           | discussions about the speed of the Rust compiler, but apart
           | from rare pathological cases, the majority of time in a
           | release build is not spent doing compile-time checks, but in
           | LLVM. Rust has zero-cost abstractions, but the zero-cost
           | refers to _runtime_ , sadly there's a lot of junk generated
           | at compile-time that LLVM has to work to remove. Which is
           | does, very well, but at cost of slower compilation.
        
             | vbezhenar wrote:
             | Is it possible to generate less junk? Sounds like compiler
             | developers took a shortcuts, which could be improved over
             | time.
        
               | rcxdude wrote:
               | Probably, but it's the kind of thing that needs a lot of
               | fairly significant overhauls in the compiler architecture
               | to really move the needle on, as far as I understand.
        
               | zozbot234 wrote:
               | You can address the junk problem manually by having
               | generic functions delegate as much of their work as
               | possible to non-generic or "less" generic functions
               | (Where a "less" generic function is one that depends only
               | on a known subset of type traits, such as size or
               | alignment. Then delegating can help the compiler generate
               | fewer redundant copies of your code, even if it can't
               | avoid code monomorphization altogether.)
        
               | andrepd wrote:
               | Isn't something like this blocked on the lack of
               | specialisation?
        
               | dwattttt wrote:
               | I believe the specific advice they're referring to has
               | been stable for a while. You take your generic function &
               | split it into a thin generic wrapper, and a non-generic
               | worker.
               | 
               | As an example, say your function takes anything that can
               | be turned into a String. You'd write a generic wrapper
               | that does the ToString step, then change the existing
               | function to just take a String. That way when your
               | function is called, only the thin outer function is
               | monomorphised, and the bulk of the work is a single
               | implementation.
               | 
               | It's not _that_ commonly known, as it only becomes a
               | problem for a library that becomes popular.
        
               | estebank wrote:
               | To illustrate:                 fn foo<S: Into<String>>(s:
               | S) {           fn inner(s: String) { ... }
               | inner(s.into())       }
        
               | LtdJorge wrote:
               | Well, zero-cost abstractions are still abstractions. It's
               | not junk per-se, but things that will be optimized out if
               | the IR has enough information to safely do so, so
               | basically lots of extra metadata to actually prove to
               | LLVM that these things are safe.
        
           | jvanderbot wrote:
           | If you'd like the rust compiler to operate quickly:
           | 
           | * Make no nested types - these slow compiler time _a lot_
           | 
           | * Include no crates, or ones that emphasize compiler speed
           | 
           | C is still v. fast though. That's why I love it (and Rust).
        
             | windward wrote:
             | >Make no nested types
             | 
             | I wouldn't like it _that much_
        
         | Aurornis wrote:
         | > makes me wonder why other languages like Rust and Swift can't
         | just do something similar to achieve similar speeds.
         | 
         | One of the primary features of Rust is the extensive compile-
         | time checking. Monomorphization is also a complex operation,
         | which is not exclusive to Rust.
         | 
         | C compile times should be very fast because it's a relatively
         | low-level language.
         | 
         | On the grand scale of programming languages and their compile-
         | time complexity, C code is closer to assembly language than
         | modern languages like Rust or Swift.
        
         | ceronman wrote:
         | I bet that if you take those 278k lines of code and rewrite
         | them in simple Rust, without using generics, or macros, and
         | using a single crate, without dependencies, you could achieve
         | very similar compile times. The Rust compiler can be very fast
         | if the code is simple. It's when you have dependencies and
         | heavy abstractions (macros, generics, traits, deep dependency
         | trees) that things become slow.
        
           | 90s_dev wrote:
           | I can't help but think the borrow checker alone would slow
           | this down by at least 1 or 2 orders of magnitude.
        
             | steveklabnik wrote:
             | Your intuition would be wrong: the borrow checker does not
             | take much time at all.
        
             | FridgeSeal wrote:
             | Again, as this been often repeated, and backed up with
             | data, the borrow-checker is a tiny fraction of a Rust apps
             | build time, the biggest chunk of time is spent in LLVM.
        
             | tomjakubowski wrote:
             | The borrow checker is really not that expensive. On a
             | random example, a release build of the regex crate, I see
             | <1% of time spent in borrowck. >80% is spent in codegen and
             | LLVM.
        
           | taylorallred wrote:
           | I'm curious about that point you made about dependencies.
           | This Rust project (https://github.com/microsoft/edit) is made
           | with essentially no dependencies, is 17,426 lines of code,
           | and on an M4 Max it compiles in 1.83s debug and 5.40s
           | release. The code seems pretty simple as well. Edit: Note
           | also that this is 10k more lines than the OP's project. This
           | certainly makes those deps suspicious.
        
             | MindSpunk wrote:
             | The 'essentially no dependencies' isn't entirely true. It
             | depends on the 'windows' crate, which is Microsoft's auto-
             | generated Win32 bindings. The 'windows' crate is huge, and
             | would be leading to hundreds of thousands of LoC being
             | pulled in.
             | 
             | There's some other dependencies in there that are only used
             | when building for test/benchmarking like serde, zstd, and
             | criterion. You would need to be certain you're building
             | only the library and not the test harness to be sure those
             | aren't being built too.
        
         | maxk42 wrote:
         | Rust is doing a lot more under the hood. C doesn't track
         | variable lifetimes, ownership, types, generics, handle
         | dependency management, or handle compile-time execution (beyond
         | the limited language that is the pre-compiler). The rust
         | compiler also makes intelligent (scary intelligent!)
         | suggestions when you've made a mistake: it needs a lot of
         | context to be able to do that.
         | 
         | The rust compiler is actually pretty fast for all the work it's
         | doing. It's just an absolutely insane amount of additional
         | work. You shouldn't expect it to compile as fast as C.
        
         | vbezhenar wrote:
         | I encountered one project in 2000-th with few dozens of KLoC in
         | C++. It compiled in a fraction of a second on old computer. My
         | hello world code with Boost took few seconds to compile. So
         | it's not just about language, it's about structuring your code
         | and using features with heavy compilation cost. I'm pretty sure
         | that you can write Doom with C macros and it won't be fast. I'm
         | also pretty sure, that you can write Rust code in a way to
         | compile very fast.
        
           | taylorallred wrote:
           | I'd be very interested to see a list of features/patterns and
           | the cost that they incur on the compiler. Ideally, people
           | should be able to use the whole language without having to
           | wait so long for the result.
        
             | kccqzy wrote:
             | Templates as one single feature can be hugely variable. Its
             | effect on compilation time can be unmeasurable. Or you can
             | easily write a few dozen lines that will take hours to
             | compile.
        
             | vbezhenar wrote:
             | So there are few distinctive patterns I observed in that
             | project. Please note that many of those patterns are
             | considered anti-patterns by many people, so I don't
             | necessarily suggest to use them.
             | 
             | 1. Use pointers and do not include header file for class,
             | if you need pointer to that class. I think that's a pretty
             | established pattern in C++. So if you want to declare
             | pointer to a class in your header, you just write `class
             | SomeClass;` instead of `#include "SomeClass.hpp"`.
             | 
             | 2. Do not use STL or IOstreams. That project used only libc
             | and POSIX API. I know that author really hated STL and
             | considered it a huge mistake to be included to the standard
             | language.
             | 
             | 3. Avoid generic templates unless absolutely necessary.
             | Templates force you to write your code in header file, so
             | it'll be parsed multiple times for every include, compiled
             | to multiple copies, etc. And even when you use templates,
             | try to split the class to generic and non-generic part, so
             | some code could be moved from header to source. Generally
             | prefer run-time polymorphism to generic compile-time
             | polymorphism.
        
               | dieortin wrote:
               | Why use C++ at that point? Also, pre declaring classes
               | instead of including the corresponding headers has quite
               | a few drawbacks.
        
               | kortilla wrote:
               | RAII? shared pointers?
        
               | maccard wrote:
               | References, for one. Also there's a huge difference
               | between "avoid templates unless necessary" and "don't use
               | templates".
        
             | LtdJorge wrote:
             | There is an experimental Cranelift backend[0] for rustc to
             | improve compilation performance in debug builds.
             | 
             | https://github.com/rust-lang/rustc_codegen_cranelift
        
           | herewulf wrote:
           | My anecdata would be that the average C++ developer puts
           | includes inside of every header file which includes more
           | headers to the point where everything is including everything
           | else and a single .cpp file draws huge swaths of unnecessary
           | code in and the project takes eons to compile on a _fast_
           | computer.
           | 
           | That's my 2000s development experience. Fortunately I've
           | spent a good chunk of the 2010s and most of the 2020s using
           | other languages.
           | 
           | The classic XKCD compilation comic exists for a reason.
        
         | glandium wrote:
         | That is kind of surprising. The sqlite "unity" build, has about
         | the same number of lines of C and takes a lot longer than that
         | to compile.
        
         | ben-schaaf wrote:
         | Every claim I've seen about unity builds being fast just never
         | rings true to me. I just downloaded the rad debugger and ran
         | the build script on a 7950x (about as fast as you can get). A
         | debug build took 5s, a release build 34s with either gcc or
         | clang.
         | 
         | Maybe it's a MSVC thing - it does seem to have some multi-
         | threading stuff. In any case raddbg non-clean builds take
         | longer than any of my rust projects.
        
           | maccard wrote:
           | I use unity builds day in day out. The speed up is an order
           | of magnitude on a 2m+ LOC project.
           | 
           | If you want to see the difference download unreal engine and
           | compile the editor with and without unity builds enabled.
           | 
           | My experience has been the polar opposite of yours - similar
           | size rust projects are an order of magnitude slower than C++
           | ones. Could you share an example of a project to compare
           | with?
        
             | almostgotcaught wrote:
             | How many LOC is unreal? I'm trying to estimate whether
             | making LLVM compatible with UNITY_BUILD would be worth the
             | effort.
             | 
             | EDIT: i signed up to get access to unreal so take a look at
             | how they do unity builds and turns out they have their own
             | build tool (not CMake) that orchestrates the build. so does
             | anyone know (can someone comment) whether unity builds for
             | them (unreal) means literally one file for literally all
             | project sources files or if it's "higher-granularity" like
             | UNITY_BUILD in CMake (i.e., single file per object).
        
               | Culonavirus wrote:
               | At least 10M (from what I remember, maybe more now)
        
               | maccard wrote:
               | The build tool groups files into a _roughly_ equivalent
               | size based on file length, and dispatches compiles those
               | in parallel.
        
               | almostgotcaught wrote:
               | how many groups do people usually use to get a fast build
               | (alternatively what is the group size)?
        
               | maccard wrote:
               | It's about 300kb before pre processor expansion by
               | default. I've never changed it.
        
             | ben-schaaf wrote:
             | > If you want to see the difference download unreal engine
             | and compile the editor with and without unity builds
             | enabled.
             | 
             | UE doesn't use a full unity build, it groups some files
             | together into small "modules". I can see how this approach
             | may have some benefits; you're trading off a faster clean
             | build for a slower incremental build.
             | 
             | I tested compiling UnrealFrontend, and a default setup with
             | the hybrid unity build took 148s. I noticed it was only
             | using half my cores due to memory constraints. I disabled
             | unity and upped the parallelism and got 182s, so 22% slower
             | while still using less memory. A similarly configured unity
             | build was 108s, so best case is ~2x.
             | 
             | On the other hand only changing the file
             | TraceTools/SFilterPreset.cpp resulted in 10s compilation
             | time under a unity build, and only 2s without unit.
             | 
             | I can see how this approach has its benefits (and
             | drawbacks). But to be clear this isn't what projects like
             | raddbg and sqlite3 are doing. They're doing a single
             | translation unit for the entire project. No parallelism, no
             | incremental builds, just a single compiler invocation. This
             | is usually what I've seen people mean by a unity build.
             | 
             | > My experience has been the polar opposite of yours -
             | similar size rust projects are an order of magnitude slower
             | than C++ ones. Could you share an example of a project to
             | compare with?
             | 
             | I just did a release build of egui in 35s, about the same
             | as raddbg's release build. This includes compiling
             | dependencies like wgpu, serde and about 290 other
             | dependencies which add up to well over a million lines of
             | code.
             | 
             | Note I do have mold configured as my linker, which speeds
             | things up significantly.
        
           | taylorallred wrote:
           | This is true. After making my earlier comment, I went home
           | and tested MSVC and Clang and got similar numbers. I had 1.5s
           | in my head from using it earlier but maybe some changes made
           | it slower. Either way, it's a lot of code and stays on the
           | order of seconds or tens of seconds rather than minutes.
        
         | 1vuio0pswjnm7 wrote:
         | Alpha. Windows-only.
         | 
         | https://codeload.github.com/EpicGamesExt/raddebugger/tar.gz/...
        
         | troupo wrote:
         | There's also Jonathan Blow's jai where he routinely builds an
         | entire game from scratch in a few seconds (hopefully public
         | beta will be released by the end of this year).
        
         | rowanG077 wrote:
         | C hardly requires any high effort compile things. No templates,
         | no generics, super simple types, no high level structures.
        
           | dgb23 wrote:
           | Are we seeing similar compilation speed when a Rust program
           | doesn't use these types of features?
        
         | weinzierl wrote:
         | This is sometimes called _amalgamation_ and you can do it Rust
         | as well. Either manually or with tools. The point is that apart
         | from very specific niches it is just not a practical approach.
         | 
         | It's not that it can't be done but that it usually is not worth
         | the hassle and our goal should be for compilation to be fast
         | despite not everything being in one file.
         | 
         | Turbo Pascal is a prime example for a compiler that won the
         | market not least because of its - for the time - outstanding
         | compilation speed.
         | 
         | In the same vein, a language can be designed for fast
         | compilation. Pascal in general was designed for single-pass
         | compilation which made it naturally fast. All the necessary
         | forward declarations were a pain though and the victory of
         | languages that are not designed for single-pass compilation
         | proofs that while doable it was not worth it in the end.
        
         | TZubiri wrote:
         | I guess you can do that, but if for some reason you needed to
         | compile separately, (suppose you sell the system to a third
         | party to a client, and they need to modify module 1, module 2
         | and the main loop.) It would be pretty trivial to remove some
         | #include "module3.c" lines and add some -o module3 options to
         | the compiler. Right?
         | 
         | I'm not sure what Rust or docker have to do with this basic
         | issue, it just feels like young blood attempting 2020 solutions
         | before exploring 1970 solutions.
        
         | john-h-k wrote:
         | My C compiler, which is pretty naive and around ~90,000 lines,
         | can compile _itself_ in around 1 second. Clang can do it in
         | like 0.4.
         | 
         | The simple truth is a C compiler doesn't need to do very much!
        
         | motorest wrote:
         | > This seems like a clear case-study that compilation can be
         | incredibly fast (...)
         | 
         | Have you tried troubleshooting a compiler error in a unity
         | build?
         | 
         | Yeah.
        
           | moffkalast wrote:
           | It compiles in 2 seconds! Does it run? No, but it was fast!
        
       | charcircuit wrote:
       | Why doesn't the Rust ecosystem optimize around compile time? It
       | seems a lot of these frameworks and libraries encourage doing
       | things which are slow to compile.
        
         | steveklabnik wrote:
         | The ecosystem is vast, and different people have different
         | priorities. Simple as that.
        
         | nicoburns wrote:
         | It's starting to, but a lot of people are using Rust because
         | they need (or want) the best possible runtime performance, so
         | that tends to be prioritised a lot of the time.
        
         | int_19h wrote:
         | It would be more accurate to say that idiomatic Rust encourages
         | doing things which are slow to compile: lots of small generic
         | functions everywhere. And the most effective way to speed this
         | up is to avoid monomorphization by using RTTI to provide a
         | single generic compiled implementation that can be reused for
         | different types, like what Swift does when generics across the
         | module boundary. But this is less efficient at runtime because
         | of all the runtime checks and computations that now need to be
         | done to deal with objects of different sizes etc, many direct
         | or even inlined calls now become virtual etc.
         | 
         | Here's a somewhat dated but still good overview of various
         | approaches to generics in different languages including C++,
         | Rust, Swift, and Zig and their tradeoffs:
         | https://thume.ca/2019/07/14/a-tour-of-metaprogramming-models...
        
         | kzrdude wrote:
         | Lots of developers in the ecosystem avoid proc macros for
         | example. But going as far as avoiding monomorphisation and
         | generics is not that widespread
        
       | aappleby wrote:
       | you had a functional and minimal deployment process (compile copy
       | restart) and now you have...
        
         | canyp wrote:
         | ...Kubernetes.
         | 
         | Damn, this makes such a great ad.
        
       | OtomotO wrote:
       | It's not. It's just doing way more work than many other
       | compilers, due to a sane type system.
       | 
       | Personally I don't care anymore, since I do hotpatching:
       | 
       | https://lib.rs/crates/subsecond
       | 
       | Zig is faster, but then again, Zig isn't memory save, so
       | personally I don't care. It's an impressive language, I love the
       | syntax, the simplicity. But I don't trust myself to keep all the
       | memory relevant invariants in my head anymore as I used to do
       | many years ago. So Zig isn't for me. Simply not the target
       | audience.
        
       | o11c wrote:
       | TL;DR `async` considered harmful.
       | 
       | For all the C++ laughing in this thread, there's really only one
       | thing that makes C++ slow - non-`extern` templates - and C++
       | gives you a lot more space to speed them up than Rust does.
        
         | int_19h wrote:
         | C++ also has async these days.
         | 
         | As for templates, I can't think of anything about them that
         | would speed up things substantially wrt Rust aside from extern
         | template and manually managing your instantiations in separate
         | .cpp files. Since otherwise it's fundamentally the same problem
         | - recompiling the same code over and over again because it's
         | parametrized with different types every time.
         | 
         | Indeed, out of the box I would actually expect C++ to do worse
         | because a C++ header template has potentially different
         | environment in every translation unit in which that header is
         | included, so without precompiled headers the compiler pretty
         | much has to assume the worst...
        
           | sgt wrote:
           | What happened with Zig and async? Last I heard they might
           | never implement it.
        
       | gz09 wrote:
       | Unfortunately, removing debug symbols in most cases isn't a
       | good/useful option
        
         | magackame wrote:
         | What "most" cases are you thinking of? Also don't forget that a
         | binary that in release weights 10 MB, when compiled with debug
         | symbols can weight 300 MB, which is way less practical to
         | distribute.
        
       | kenoath69 wrote:
       | Where is Cranelift mentioned
       | 
       | My 2c on this is nearly ditching rust for game development due to
       | the compile times, in digging it turned out that LLVM is very
       | slow regardless of opt level. Indeed it's what the Jai devs have
       | been saying.
       | 
       | So Cranelift might be relevant for OP, I will shill it endlessly,
       | took my game from 16 seconds to 4 seconds. Incredible work
       | Cranelift team.
        
         | jiehong wrote:
         | I think that's what zig team is also doing to allow very fast
         | build times: remove LLVM.
        
           | norman784 wrote:
           | Yes, Zig author commented[0] that a while ago
           | 
           | [0] https://news.ycombinator.com/item?id=44390972
        
         | norman784 wrote:
         | Nice, I checked a while ago and was no support for macOS
         | aarch64, but seems that now it is supported.
        
         | lll-o-lll wrote:
         | Wait. You were going to ditch rust because of _16 second_ build
         | times?
        
           | sarchertech wrote:
           | 16 seconds is infuriating for something that needs to be
           | manually tested like does this jump feel too floaty.
           | 
           | But it's also probable that 16 seconds was fairly early in
           | development and it would get much worse from there.
        
           | metaltyphoon wrote:
           | Over time that adds up when your coding consists of REPL like
           | workflow.
        
           | kenoath69 wrote:
           | Pulling out Instagram 100 times in every workday, yes, it's a
           | total disaster
        
             | johnisgood wrote:
             | It may also contribute to smoking. :D Or (over-)eating...
             | or whatever your vice is.
        
           | Mawr wrote:
           | "Wait. You were going to ditch _subversion for git_ because
           | of 16 second _branch merge_ times? "
           | 
           | Performance matters.
        
         | BreakfastB0b wrote:
         | I participated in the most recent Bevy game jam and the
         | community has a new tool that came out of Dioxus called
         | subsecond which as the name suggests provides sub-second hot
         | reloading of systems. It made prototyping very pleasant.
         | Especially when iterating on UI.
         | 
         | https://github.com/TheBevyFlock/bevy_simple_subsecond_system
        
       | smcleod wrote:
       | I've got to say when I come across an open source project and
       | realise it's in rust I flinch a bit know how incredibly slow the
       | build process is. It's certainly been one of the deterrents to
       | learning it.
        
       | juped wrote:
       | I don't think rustc is that slow. It's usually cargo/the dozens
       | of crates that make it take a long time, even if you've set up a
       | cache and rustc is doing nothing but hitting the cache.
        
       | Devasta wrote:
       | Slow compile times are a feature, get to make a cuppa.
        
         | zozbot234 wrote:
         | > Slow compile times are a feature
         | 
         | xkcd is always relevant: https://xkcd.com/303/
        
           | randomNumber7 wrote:
           | On the other hand you get mentally insane if you try to work
           | in a way that you do s.th. usefull during the 5-10 min
           | compile times you often have with C++ projects.
           | 
           | When I had to deal with this I would just open the newspaper
           | and read an article in front of my boss.
        
             | PhilipRoman wrote:
             | Slow compile times really mess with your brain. When I
             | wanted to test two different solutions, I would keep
             | multiple separate clones (each one takes about 80GB, mind
             | you) and do the manual equivalent of branch prediction by
             | compiling both, just in case I needed the other one as
             | well.
        
       | duped wrote:
       | A lot of people are replying to the title instead of the article.
       | 
       | > To get your Rust program in a container, the typical approach
       | you might find would be something like:
       | 
       | If you have `cargo build --target x86_64-unknown-linux-musl` in
       | your build process you do not need to do this anywhere in your
       | Dockerfile. You should compile and copy into /sbin or something.
       | 
       | If you really want to build in a docker image I would suggest
       | using `cargo --target-dir=/target ...` and then run with `docker
       | run --mount type-bind,...` and then copy out of the bind mount
       | into /bin or wherever.
        
         | edude03 wrote:
         | Many docker users develop on arm64-darwin and deploy to x86_64
         | (g)libc, so I don't think that'll work generally.
        
           | duped wrote:
           | Those users are wrong :shrug:
        
         | remram wrote:
         | The author dismissed that option saying "I value that docker
         | build can have a clean environment every time", so this is
         | self-inflicted.
        
       | cratermoon wrote:
       | Some code that can make Rust compilation pathologically slow is
       | complex const expressions. Because the compiler can evaluate a
       | subset of expressions at compile time[1], a complex expression
       | can take an unbounded amount of time to evaluate. The long-
       | running-const-eval will by default abort the compilation if the
       | evaluation takes too long.
       | 
       | 1 https://doc.rust-lang.org/reference/const_eval.html
        
       | ozgrakkurt wrote:
       | Rust compiler is very very fast but language has too many
       | features.
       | 
       | The slowness is because everyone has to write code with generics
       | and macros in Java Enterprise style in order to show they are
       | smart with rust.
       | 
       | This is really sad to see but most libraries abuse codegen
       | features really hard.
       | 
       | You have to write a lot of things manually if you want fast
       | compilation in rust.
       | 
       | Compilation speed of code just doesn't seem to be a priority in
       | general with the community.
        
         | aquariusDue wrote:
         | Yeah, for application code in my experience the more I stick to
         | the dumb way to do it the less I fight the borrow checker along
         | with fewer trait issues.
         | 
         | Refactoring seems to take about the same time too so no loss on
         | that front. After all is said and done I'm just left with
         | various logic bugs to fix which is par for the course (at least
         | for me) and a sense of wondering if I actually did everything
         | properly.
         | 
         | I suppose maybe two years from now we'll have people that
         | suggest avoiding generics and tempering macro usage. These days
         | most people have heard the advice about not stressing over
         | cloning and unwraping (though expect is much better imo) on the
         | first pass more or less.
         | 
         | Something something shiny tool syndrome?
        
         | skeezyboy wrote:
         | >Compilation speed of code just doesn't seem to be a priority
         | in general with the community.
         | 
         | they have only one priority, memory safety (from a certain
         | class of memory bugs)
        
       | rednafi wrote:
       | I'm glad that Go went the other way around: compilation speed
       | over optimization.
       | 
       | For the kind of work I do -- writing servers, networking, and
       | glue code -- fast compilation is absolutely paramount. At the
       | same time, I want some type safety, but not the overly obnoxious
       | kind that won't let me sloppily prototype. Also, the GC helps. So
       | I'll gladly pay the price. Not having to deal with sigil soup is
       | another plus point.
       | 
       | I guess Google's years of experience led to the conclusion that,
       | for software development to scale, a simple type system, GC, and
       | wicked fast compilation speed are more important than raw runtime
       | throughput and semantic correctness. Given the amount of
       | networking and large - scale infrastructure software written in
       | Go, I think they absolutely nailed it.
       | 
       | But of course there are places where GC can't be tolerated or
       | correctness matters more than development speed. But I don't work
       | in that arena and am quite happy with the tradeoffs that Go made.
        
         | galangalalgol wrote:
         | That is exactly what go was meant for and there is nothing
         | better than picking the right tool for the job. The only foot
         | gun I have seen people run into is parallelism with mutable
         | shared state through channels can be subtly and exploitably
         | wrong. I don't feel like most people use channels like that
         | though? I use rust because that isn't the job I have. I usually
         | have to cramb slow algorithms into slower hardware, and the
         | problems are usually almost but not quite embarrassingly
         | parallel.
        
           | bjackman wrote:
           | I think a lot of the materials that the Go folks put out in
           | the early days encourage a very channel-heavy style of
           | programming that leads to extremely bad places.
           | 
           | Nowadays the culture seems to have evolved a bit. I now go
           | into high alert mode if I see a channel cross a function
           | boundary or a goroutine that wasn't created via errgroup or
           | similar.
           | 
           | People also seem to have chilled out about the "share by
           | communicating" thing. It's usually better to just use a mutex
           | and I think people recognise that now.
        
             | rednafi wrote:
             | This is true. I have been writing Go for years and still
             | think channel is a bit too low level. It probably would've
             | benefited from a different layer of abstraction.
        
         | ode wrote:
         | Is Go still in heavy use at Google these days?
        
           | hu3 wrote:
           | What would they use for networking if not Go?
        
             | surajrmal wrote:
             | C++ and Java. Go is still used, but it's never caught up to
             | the big two.
        
               | frollogaston wrote:
               | And probably more Java than C++
        
             | homebrewer wrote:
             | Last time I paid any attention to Google's high level
             | conference presenters (like Titus Winters), they almost
             | didn't use Go at all. Judging by the sibling comment, this
             | hasn't changed much. For some reason people are thinking
             | that half of Google is written in Go at this point, when in
             | reality if you listen to what they themselves are saying,
             | it's 99% C++ and Java, with a tiny bit of Python and other
             | languages where it makes sense.
             | 
             | It's just a project from a few very talented people who
             | happen to draw their salary from Google's coffers.
        
               | fireflash38 wrote:
               | K8s isn't entirely in go?
        
               | frollogaston wrote:
               | They don't really use K8S internally
        
           | fsmv wrote:
           | Go has never been in heavy use at Google
        
             | melodyogonna wrote:
             | Isn't it heavily used in Google Cloud?
        
         | mark38848 wrote:
         | What are obnoxious types? Types either represent the data
         | correctly or not. I think you can force types to shut up the
         | compiler in any language including Haskell, Idris,
         | PureScript...
        
           | Mawr wrote:
           | I'd say you already get like 70% of the benefit of a type
           | system with just the basic "you can't pass an int where
           | string is expected". Being able to define your own types
           | based on the basic ones, like "type Email string", so it's no
           | longer possible to pass a "string" where "Email" is expected
           | gets you to 80%. Add Result and Optional types (or arguably
           | just sum types if you prefer) and you're at 95%. Anything
           | more and you're pushing into diminishing returns.
        
             | hgomersall wrote:
             | Well it depends what you're doing. 95% is like, just your
             | opinion man. The rust type system allows, in many cases,
             | APIs that you cannot use wrongly, or are highly resistant
             | to incorrect usage, but to do that requires careful
             | thinking about. To be clear, such APIs are just as relevant
             | internally to a project as externally if you want to design
             | a system that is long term maintainable and robust and I
             | would argue is the point when the type system starts to get
             | really useful (rather than diminishing returns).
        
               | rednafi wrote:
               | > The rust type system allows, in many cases, APIs that
               | you cannot use wrongly, or are highly resistant to
               | incorrect usage, but to do that requires careful thinking
               | about
               | 
               | I need none of that guarantee and all of the compilation
               | speed along with a language where juniors in my team can
               | contribute quickly. Different problem space.
        
           | ratorx wrote:
           | This might work for the types you create, but what about all
           | the code written in the language that expects the "proper"
           | structure?
           | 
           | > Types either represent the data or not
           | 
           | This definitely required, but is only really the first step.
           | Where types get really useful is when you need to change them
           | later on. The key aspects here are how easily you can change
           | them, and how much the language tooling can help.
        
           | throwawaymaths wrote:
           | > Types either represent the data correctly or not.
           | 
           | No. two types can represent the same payload, but one might
           | be a simple structure, the other one could be three or twenty
           | nested type template abstractions deep, and created by a proc
           | macro so you can't chase down how it was made so easily.
        
         | mike_hearn wrote:
         | _> fast compilation is absolutely paramount. At the same time,
         | I want some type safety, but not the overly obnoxious kind that
         | won't let me sloppily prototype. Also, the GC helps_
         | 
         | Well, that point in the design space was already occupied by
         | Java which also has extremely fast builds. Go exists primarily
         | because the designers wanted to make a new programming
         | language, as far as I can tell. It has some nice implementation
         | aspects but it picked up its users mostly from the
         | Python/Ruby/JS world rather than C/C++/Java, which was the
         | original target market they had in mind (i.e. Google servers).
         | Scripting language users were in the market for a language that
         | had a type system but not one that was too advanced, and which
         | kept the scripting "feel" of very fast turnaround times. But
         | not Java because that was old and unhip, and all the
         | interesting intellectual space like writing libs/conf talks was
         | camped on already.
        
           | rsanheim wrote:
           | Java still had slow startup and warmup time circa 2005-2007,
           | on the order of 1-3 seconds for hello world and quite a bit
           | more for real apps. That is horrendous for anything CLI
           | based.
           | 
           | And you left out classloader/classpath/JAR dependency hell,
           | which was horrid circa late 90s/early 2000s...and I'm
           | guessing was still a struggle when Go really started
           | development. Especially at Google's scale.
           | 
           | Don't get me wrong, Java has come a long way and is a fine
           | language and the JVM is fantastic. But the java of 2025 is
           | not the same as mid-to-late 2000s.
        
             | mike_hearn wrote:
             | Maybe so, although I don't recall it being that bad.
             | 
             | But Go wasn't designed for CLI apps. It was designed for
             | writing highly multi-threaded servers at Google, according
             | to the designers, hence the focus on features like
             | goroutines. And in that context startup time just doesn't
             | matter. Startup time of servers at Google was (in that era)
             | dominated by cluster scheduling, connecting to backends,
             | loading reference data and so on. Nothing that a change in
             | programming language would have fixed.
             | 
             | Google didn't use classloader based frameworks so that also
             | wasn't relevant.
        
               | frollogaston wrote:
               | Golang is frequently used for CLIs, even if it wasn't
               | designed for that exactly
        
               | zenlot wrote:
               | If you want to write CLI tool, you use Rust.
        
               | frollogaston wrote:
               | Or you use Golang? A lot of the time there isn't a big
               | difference.
        
               | rednafi wrote:
               | Who said that? Go is pretty amazing to whip up great CLIs
               | quickly.
        
               | frollogaston wrote:
               | Yeah, I imagine if Golang didn't exist or somehow didn't
               | work for CLIs, those ones would be in Py or even JS
               | instead. I have a lot of respect for Rust, but it's more
               | deliberate.
        
           | loudmax wrote:
           | As a system administrator, I vastly prefer to deploy Go
           | programs over Java programs. Go programs are typically
           | distributed as a single executable file with no reliance on
           | external libraries. I can usually run `./program -h` and it
           | tells me about all the flags.
           | 
           | Java programs rely on the JVM, of which there are many
           | variants. Run time options are often split into multiple XML
           | files -- one file for logging, another to control the number
           | of threads and so on. Checking for the running process using
           | `ps | grep` yields some long line that wraps the terminal
           | window, or doesn't fit neatly into columns shown in `htop` or
           | `btop`.
           | 
           | These complaints are mostly about conventions and idioms, not
           | the languages themselves. I appreciate that the Java
           | ecosystem is extremely powerful and flexible. It is possible
           | to compile Java programs into standalone binaries, though I
           | rarely see these in practice. Containers can mitigate the
           | messiness, and that helps, up until the point when you need
           | to debug some weird runtime issue.
           | 
           | I wouldn't argue that people should stop programming in Java,
           | as there are places where it really is the best choice. For
           | example deep legacy codebases, or where you need the power of
           | the JVM for dynamic runtime performance optimizations.
           | 
           | There are a lot of places where Go is the best choice (eg.
           | simple network services, CLI utilities), and in those cases,
           | please, please deploy simple Go programs. Most of the time,
           | developers will reach for whatever language they're most
           | comfortable with.
           | 
           | What I like most about Go is how convenient it is, by
           | default. This makes a big difference.
        
           | k__ wrote:
           | _" it picked up its users mostly from the Python/Ruby/JS
           | world rather than C/C++/Java"_
           | 
           | And with the increasing performance of Bun, it seems that Go
           | is about to get a whooping by JS.
           | 
           | (Which isn't really true, as most of the Bun perf comes from
           | Zig, but they are targeting JS Devs.)
        
             | rednafi wrote:
             | Runtimes like Bun, Deno, or type systems like TypeScript
             | don't change the fact it's still JS underneath -- a
             | crappily designed language that should've never left
             | throwable frontend code.
             | 
             | None of these runtimes make JS anywhere even close to
             | single-threaded Go perf, let alone multithreaded
             | (goroutine) perf.
        
               | frollogaston wrote:
               | JS is perfectly designed for what it does, frontend and
               | non-CPU-intensive backend code. It's never going to reach
               | singlethreaded Golang perf.
        
               | rednafi wrote:
               | JavaScript was never designed for non-browser usage. It's
               | the community's unquenchable thirst to use the same
               | language everywhere that brought us here.
        
               | frollogaston wrote:
               | NodeJS about page makes its case pretty succinctly, JS
               | was a good fit for IO-bound concurrent backends because
               | of the event loop. This was at a time when no other major
               | language/runtime had a good answer for this unless you
               | count Erlang. Plenty of people using it didn't even come
               | from the web frontend side, myself included.
               | 
               | npm was also maybe the first default package manager that
               | "just works," unlike Python or browser JS.
        
               | AgentME wrote:
               | A lot of people don't realize NodeJS was made because the
               | creator wanted to make a runtime dedicated to
               | asynchronous IO, didn't want to use a language with a
               | pre-existing standard library and ecosystem built around
               | synchronous IO, and realized that JS almost uniquely fit
               | the bill. It was not built for the purpose of letting
               | people who only knew JS use JS on the server.
        
               | frollogaston wrote:
               | This tracks, cause the NodeJS website also doesn't
               | mention anything about people already knowing JS. And
               | imports worked completely differently between browser and
               | Node, so they were pretty separate things at least at the
               | start.
        
           | rednafi wrote:
           | Java absolutely does not fill in the niche that Go targeted.
           | Even without OO theology, JVM applications are heavy and
           | memory intensive. Plus the startup time of the VM alone is a
           | show stopper for the type of work I do. Also yes, Java isn't
           | hip and you couldn't pay me to write it anymore.
        
           | frollogaston wrote:
           | Golang having solid n:m greenthreading day 1 was its big
           | deal. Java has had no good way to do IO-heavy multitasking,
           | leading to all those async/promise frameworks that jack up
           | your code. I cannot even read the Java code we have at work.
           | Java recently got virtual threads, but even if that fixes the
           | problem, it'll be a while before things change to that.
           | Python had the same problem before asyncio. This isn't even a
           | niche thing, your typical web backend needs cooperative
           | multitasking.
           | 
           | I'm also not fond of any of the Golang syntax, especially not
           | having exceptions. Or if you want explicit errors, fine, at
           | least provide nice unwrap syntax like Rust does.
        
             | aaronblohowiak wrote:
             | by FAR my biggest complaint about Golang was null instead
             | of Option. could have been special cased like their slice
             | and map and would have been so so so much better than nil
             | checks imho. really, a big miss.
        
             | cogman10 wrote:
             | Java 21 has n:m green threads, but with caveats. Java 24
             | removed a major source of the caveats.
        
               | frollogaston wrote:
               | Yeah, I'm hoping that fixes things eventually. We still
               | can't use that at work, and even once we can, there's
               | 10yo code still using 3 different old ways of async.
        
               | computably wrote:
               | I'm sure you're already aware but for those who aren't -
               | Java 21 was released in 2023. Golang 1.0 was released in
               | 2012.
        
         | silverwind wrote:
         | You can have the best of both worlds: A fast, but sloppy
         | compiler and slow, but thorough checkers/linters. I think it's
         | ideal that way, but rust seems to have chosen to needlessly
         | combine both actions into one.
        
         | paldepind2 wrote:
         | > I guess Google's years of experience led to the conclusion
         | that, for software development to scale, a simple type system,
         | GC, and wicked fast compilation speed are more important than
         | raw runtime throughput and semantic correctness.
         | 
         | I'm a fan of Go, but I don't think it's the product of some
         | awesome collective Google wisdom and experience. Had it been, I
         | think they'd have come to the conclusion that statically
         | eliminating null pointer exceptions was a worthwhile endeavor,
         | just to mention one thing. Instead, I think it's just the
         | product of some people at Google making a language they way
         | they wanted to.
        
           | melodyogonna wrote:
           | But those people at Google were veteran researchers who
           | wanted to make a language that could scale for Google's use
           | cases; these things are well documented.
           | 
           | For example, Ken Thompson has said his job at Google was just
           | to find things he could make better.
        
             | nine_k wrote:
             | They also built a language that can be learned in a weekend
             | (well, now two) and is small enough for a fresh grad hire
             | to learn at the job.
             | 
             | Go has a very low barrier to entry, but also a relatively
             | low ceiling. The proliferation of codegen tools for Go is a
             | testament of its limited expressive power.
             | 
             | It doesn't mean that Go didn't hit a sweet spot. For
             | certain tasks, it very much did.
        
         | danielscrubs wrote:
         | One day I would like to just change pascals syntax a bit to be
         | Pythonic and just blow the socks of junior and Go developers.
        
           | the_sleaze_ wrote:
           | That's what they did to Erlang with Elixir and now there are
           | a lot of people saying it's the Greatest Of All Time.
           | 
           | I'd be interested in this project if you do decide to pursue
           | it.
        
           | rednafi wrote:
           | Sounds like the guy who wanted to write curl in a weekend. /s
        
         | frollogaston wrote:
         | Same but with Python and NodeJS cause I'm doing less
         | performance-critical stuff. Dealing with type safety and slow
         | builds would cost way more than it's worth.
        
           | rednafi wrote:
           | Python and NodeJS bring a whole lot of other problems. But
           | yeah at a smaller scale these languages are faster to work
           | with.
           | 
           | At the same time, I have worked at places where people had to
           | rewrite major parts of their backend in other languages
           | because Python/Node was no longer sufficient.
        
             | frollogaston wrote:
             | I'd have to see it, cause rewrites happen all the time. We
             | had a system written in C++, then rewritten in Golang
             | because C++ was supposedly not good enough, then rewritten
             | back into C++.
        
         | liampulles wrote:
         | As the story goes, a couple of Google developers designed Go
         | while waiting for one of their C++ projects to compile.
        
           | zenlot wrote:
           | If we wanted speed compile times only, we'd be using Pascal.
           | No need for Go. In fact, if there would be ever a case for me
           | to use Go, I'd rather go for Pascal or Delphi. But there
           | isn't, it just doesn't fit anywhere.
        
             | rednafi wrote:
             | I understand the sentiment as I feel the same about Rust.
             | I'd rather raw dog C++ than touch Rust. Doesn't make sense
             | and I could come up with some BS like you did and make my
             | case anyway.
        
       | jmyeet wrote:
       | Early design decisions favored run-time over compile-time [1]:
       | 
       | > * Borrowing -- Rust's defining feature. Its sophisticated
       | pointer analysis spends compile-time to make run-time safe.
       | 
       | > * Monomorphization -- Rust translates each generic
       | instantiation into its own machine code, creating code bloat and
       | increasing compile time.
       | 
       | > * Stack unwinding -- stack unwinding after unrecoverable
       | exceptions traverses the callstack backwards and runs cleanup
       | code. It requires lots of compile-time book-keeping and code
       | generation.
       | 
       | > * Build scripts -- build scripts allow arbitrary code to be run
       | at compile-time, and pull in their own dependencies that need to
       | be compiled. Their unknown side-effects and unknown inputs and
       | outputs limit assumptions tools can make about them, which e.g.
       | limits caching opportunities.
       | 
       | > * Macros -- macros require multiple passes to expand, expand to
       | often surprising amounts of hidden code, and impose limitations
       | on partial parsing. Procedural macros have negative impacts
       | similar to build scripts.
       | 
       | > * LLVM backend -- LLVM produces good machine code, but runs
       | relatively slowly. Relying too much on the LLVM optimizer -- Rust
       | is well-known for generating a large quantity of LLVM IR and
       | letting LLVM optimize it away. This is exacerbated by duplication
       | from monomorphization.
       | 
       | > * Split compiler/package manager -- although it is normal for
       | languages to have a package manager separate from the compiler,
       | in Rust at least this results in both cargo and rustc having
       | imperfect and redundant information about the overall compilation
       | pipeline. As more parts of the pipeline are short-circuited for
       | efficiency, more metadata needs to be transferred between
       | instances of the compiler, mostly through the filesystem, which
       | has overhead.
       | 
       | > * Per-compilation-unit code-generation -- rustc generates
       | machine code each time it compiles a crate, but it doesn't need
       | to -- with most Rust projects being statically linked, the
       | machine code isn't needed until the final link step. There may be
       | efficiencies to be achieved by completely separating analysis and
       | code generation.
       | 
       | > * Single-threaded compiler -- ideally, all CPUs are occupied
       | for the entire compilation. This is not close to true with Rust
       | today. And with the original compiler being single-threaded, the
       | language is not as friendly to parallel compilation as it might
       | be. There are efforts going into parallelizing the compiler, but
       | it may never use all your cores.
       | 
       | > * Trait coherence -- Rust's traits have a property called
       | "coherence", which makes it impossible to define implementations
       | that conflict with each other. Trait coherence imposes
       | restrictions on where code is allowed to live. As such, it is
       | difficult to decompose Rust abstractions into, small, easily-
       | parallelizable compilation units.
       | 
       | > * Tests next to code -- Rust encourages tests to reside in the
       | same codebase as the code they are testing. With Rust's
       | compilation model, this requires compiling and linking that code
       | twice, which is expensive, particularly for large crates.
       | 
       | [1]: https://www.pingcap.com/blog/rust-compilation-model-
       | calamity...
        
       | edude03 wrote:
       | First time someone I know in real life has made it to the HN
       | front page (hey sharnoff, congrats) anyway -
       | 
       | I think this post (accidentally?) conflates two different sources
       | of slowness:
       | 
       | 1) Building in docker 2) The compiler being "slow"
       | 
       | They mention they could use bind mounts, yet wanting a clean
       | build environment - personally, I think that may be misguided.
       | Rust with incremental builds is actually pretty fast and the time
       | you lose fighting dockers caching would likely be made up in
       | build times - since you'd generally build and deploy way more
       | often than you'd fight the cache (which, you'd delete the cache
       | and build from scratch in that case anyway)
       | 
       | So - for developers who build rust containers, I highly recommend
       | either using cache mounts or building outside the container and
       | adding just the binary to the image.
       | 
       | 2) The compiler being slow - having experienced ocaml, go and
       | scala for comparisons the rust compiler is slower than go and
       | ocaml, sure, but for non interactive (ie, REPL like) workflows,
       | this tends not to matter in my experience - realistically, using
       | incremental builds in dev mode takes seconds, then once the code
       | is working, you push to CI at which point you can often accept
       | the (worst case?) scenario that it takes 20 minutes to build your
       | container since you're free to go do other things.
       | 
       | So while I appreciate the deep research and great explanations, I
       | don't think the rust compiler is actually slow, just slower than
       | what people might be use to coming from typescript or go for
       | example.
        
       | leoh wrote:
       | tl;dr: it's slow because it finds far more bugs before runtime
       | than literally any other mainstream compiled language
        
       | ac130kz wrote:
       | tldr as always, don't use Musl, if you want performance,
       | compatibility.
        
         | ac130kz wrote:
         | Some "smart" folks even downvote this advice. Yeah, I've seen
         | articles on musl's horrible performance back in 2017-2018, and
         | apparently it still holds, yet I get a downvote.
        
           | 9rx wrote:
           | You make it sound like a "downvote" has meaning or something.
           | 
           | It doesn't.
        
       | amelius wrote:
       | Meanwhile, other languages have a JIT compiler which compiles
       | code as it runs. This would be great for development even if it
       | turns out to be slower overall.
        
         | akkad33 wrote:
         | Actually JITs can be faster than AOT compilation because they
         | can be optimized for the current architecture they are running
         | in. There were claims Julia, a JIT language can beat C in some
         | benchmarks
        
           | amelius wrote:
           | In fact, JITs can be faster because they can specialize code,
           | i.e. make optimizations based on live data.
        
       | feelamee wrote:
       | > Vim hangs when you open it
       | 
       | you can enable word wrapping as a workaround ( `:set wrap`).
       | Lifehack: it can be hard to navigate in such file with just `h,
       | j, k, l`, but you can use `gh, gj, etc`. With `g` vim will work
       | with visual lines, while without it with just lines splitted with
       | LF/CRLF
        
         | mmh0000 wrote:
         | With a little bit of vimrc magic you can make it transparent:
         | "Make k/j up/down work more naturally by going to the next
         | displayed line vs       "going to the next logical line (for
         | when word-wrapping is on):       noremap k gk       noremap j
         | gj       noremap <up> gk       noremap <down> gj       "Same as
         | above, but for arrow keys in insert mode:       inoremap <up>
         | <Esc>gka       inoremap <down> <Esc>gja
        
       | TZubiri wrote:
       | >Every time I wanted to make a change, I would:
       | 
       | >Build a new statically linked binary (with
       | --target=x86_64-unknown-linux-musl) >Copy it to my server
       | >Restart the website
       | 
       | Isn't it a basic C compiler feature that you can compile a file
       | as an Object, and then link the objects into a single executable?
       | Then you only recompile the file you changed.
       | 
       | Not sure what I'm missing.
        
         | pornel wrote:
         | That's how Rust works already.
         | 
         | The problem has been created by Docker which destroys all of
         | the state. If this was C, you'd also end up losing all of the
         | object files and rebuilding them every time.
        
           | TZubiri wrote:
           | Nope, reread the article, docker wasn't part of the problem
           | it's part of the 'solution' according to OP.
        
       | s_ting765 wrote:
       | OP could have skipped all this by doing the compilation with
       | cache on the host system and copying the compiled statically
       | linked binary back to the docker image build.
        
       | mellosouls wrote:
       | Related from a couple of weeks ago:
       | 
       | https://news.ycombinator.com/item?id=44234080
       | 
       |  _(Rust compiler performance; 287 points, 261 comments)_
        
       | jeden wrote:
       | why rust compiler create so BIG executable!
        
       | fschuett wrote:
       | For deploying Rust servers, I use Spin WASM functions[1], so no
       | Docker / Kubernetes is necessary. Not affiliated with them, just
       | saying. I just build the final WASM binary and then the rest is
       | managed by the runtime.
       | 
       | Sadly, the compile time is just as bad, but I think in this case
       | the allocator is the biggest culprit, since disabling
       | optimization will degrade run-time performance. The Rust team
       | should maybe look into shipping their own bundled allocator,
       | "native" allocators are highly unpredictable.
       | 
       | [^1]: https://www.fermyon.com
        
       | dminik wrote:
       | One aspect that I find interesting is that Rust projects often
       | seem deceivingly small.
       | 
       | First, dependencies don't translate easily to the perceived size.
       | In C++ dependencies on large projects are often vendored (or even
       | not used at all). And so it is easy to look at your ~400000 line
       | codebase and go "it's slow, but there's a lot of code here after
       | all".
       | 
       | Second (and a much worse problem) are macros. They actually hit
       | the same issue. A macro that expands to 10s or 100s of lines can
       | very quickly take your 10000 line project and turn it into a
       | million line behemoth.
       | 
       | Third are generics. They also suffer the same problem. Every
       | generic instantiation is eating your CPU.
       | 
       | But I do want to offer a bit of an excuse for rust. Because these
       | are great features. They turn what would have taken 100000 lines
       | of C or 25000 lines of C++ to a few 1000s lines of Rust.
       | 
       | However, there is definitely an overuse here making the ecosystem
       | seem slow. For instance, at work we use async-graphql. The
       | library itself is great, but it's an absolute proc-macro hog.
       | There's issues in the repository open for years about the
       | performance. You can literally feel the compiler getting slower
       | for each type you add.
        
         | jvanderbot wrote:
         | You can literally feel the compiler getting slower for each
         | type you add.
         | 
         | YSK: The compiler performance is IIRC exponential in the
         | "depth" of types. And oh boy does GraphQL love their nested
         | types.
        
         | epage wrote:
         | > Second (and a much worse problem) are macros. They actually
         | hit the same issue. A macro that expands to 10s or 100s of
         | lines can very quickly take your 10000 line project and turn it
         | into a million line behemoth.
         | 
         | Recently, support as added to help people analyze this
         | 
         | See https://nnethercote.github.io/2025/06/26/how-much-code-
         | does-...
        
         | 1vuio0pswjnm7 wrote:
         | "They turn what would have taken 100000 lines of C or 25000
         | lines of C++ to a few 1000s lines of Rust."
         | 
         | How do they do with smaller programs that would have taken far
         | less than 100,000 lines of C.
         | 
         | For whatever reasons, many authors choose to rewrite small C
         | utilties, or create similar ones, using Rust.^1 Perhaps there
         | are countless examples of 100,000 line C programs rewritten in
         | Rust but the ones I see continually submitted to HN, Github and
         | elsewhere are much smaller.
         | 
         | How does Rust compilation time compare with C for smaller
         | programs.
         | 
         | NB. I am not asking about program size. I am asking about
         | compilation speed.
         | 
         | (Also curious about resource usage, e.g. storage, memory. For
         | example, last time I measured, Rust compiler toolchain is about
         | 2x as large as the GCC toolchain I am using.)
         | 
         | 1. Some of these programs due to their size seem unlikely to
         | have undetected memory-safety issues in any language. Their
         | size makes them relatively easy to audit. Unlike a 100,000 line
         | C program.
        
           | lor_louis wrote:
           | I write a lot of C and Rust, and my personal experience is
           | that for smaller C programs, Rust tends to have a slightly
           | higher line count, but it's mostly due to forcing the user to
           | handle every error possible.
           | 
           | A truly robust C program will generally be much larger than
           | the equivalent Rust program.
        
       | WalterBright wrote:
       | I suspect that it is because the borrow checker needs to do data
       | flow analysis. DFA is normally done in the optimizer, and we all
       | know that optimized builds are quite a bit slower, and that is
       | due to DFA.
       | 
       | DFA in the front end means slow.
        
         | steveklabnik wrote:
         | As said many times in this thread already, the borrow checker
         | is virtually never a significant amount of time spent while
         | compiling.
         | 
         | Also, the article goes into great depth as to what is happening
         | there, and the borrow checker never comes up.
        
           | WalterBright wrote:
           | That makes me curious as to how Rust does the DFA. What I do
           | is construct the data flow equations as bit vectors, which
           | are then solved iteratively until a solution is converged on.
           | 
           | Doing this on the whole program eats a lot of memory.
        
             | steveklabnik wrote:
             | I'm not an expert on this part of the compiler, but what I
             | can tell you is that Rust uses multiple forms of IR, and
             | the IR that the borrow checker operates on (https://rustc-
             | dev-guide.rust-lang.org/mir/index.html) already encodes
             | control flow. So doing that construction, at least, is just
             | a normal part of the compiler, and isn't part of the borrow
             | checker itself.
             | 
             | However, in my understanding, it takes that IR, does build
             | a borrow graph, computes liveliness, and then does
             | inference. There's been a number of details in how this has
             | changed over the years, but it's vaguely datalog shaped.
        
       | Scuds wrote:
       | I have a mac m4 pro and it's 2 minutes to compile all of Deno,
       | which is my go-to for bigass rust projects.
       | 
       | ```
       | 
       | > cargo clean && time cargo build
       | 
       | cargo build 713.80s user 91.57s system 706% cpu 1:53.97 total
       | 
       | > cargo clean && time cargo build --release
       | 
       | cargo build --release 1619.53s user 142.65s system 354% cpu
       | 8:17.05 total
       | 
       | ```
       | 
       | this is without incremental compilation. And it's not like you
       | have to babysit a release build if you have a CI/CD system
        
         | hu3 wrote:
         | Interesting, M1 Max took 6 minutes and M1 Air took 11 minutes
         | according to this article:
         | 
         | https://corrode.dev/blog/tips-for-faster-rust-compile-times/...
        
       ___________________________________________________________________
       (page generated 2025-06-27 23:01 UTC)