HNHacker News
TopNewBestAskShowJobs

strager

300 karma · joined January 27, 2011

strager.nds@gmail.com http://strager.net/
submissionscomments
strager··on A practical comparison of build and test speed between C++ and Rust
You're welcome! But maybe my code (written by a Rust newbie) isn't the best to learn from... ;P
strager··on A practical comparison of build and test speed between C++ and Rust
For the 17k SLOC project, link times were already under 100 milliseconds without mold. (EDIT: Actually linking took 129 milliseconds with GNU ld.) There are other, bigger bottlenecks to deal with.

As a project grows larger, link times become more noticeable, and mold starts to help.

strager··on A practical comparison of build and test speed between C++ and Rust
Thank you for your kind words! I appreciate your empathy. This project took about 7 weeks (including trimming, porting, benchmarking, optimizing, and finally writing the article).
strager··on A practical comparison of build and test speed between C++ and Rust
Ah, good point. I hadn't considered this.
strager··on A practical comparison of build and test speed between C++ and Rust
> unsafe and passing pointers everywhere?

Do you think this would impact Rust build times?

> Reimplementing vectors and linked lists.

Someone has to implement them.

Note that they're also implemented in the C++ code, so the comparison is fair.

> Also, makes use of a ton of macros.

Can you show an example where the Rust code uses "a ton of macros"? The only place I can think of is the tests, but I thought macros were what you're supposed to use in Rust for assertions.

If you're referring to proc macros, I thought those were very popular in Rust.

strager··on A practical comparison of build and test speed between C++ and Rust
> Note how the OP uses an M1 laptop w/64MB of ram.

I tested with two different machines:

* Linux machine with an AMD Ryzen 9 5950X

* macOS machine with an M1 Max

Each chart is labelled with 'Linux' or 'macOS'.

> If you want to compile a monolithic web application written in Rust within a reasonable amount of time, you need that much top-of-the-line hardware for your work. You're still waiting several minutes to compile, but compile times get even worse with older hardware running debian and mold/lld. CI test/build runs also take a long time to complete.

This is true with C++ as well. The article focused on the build-test development cycle, but you're right that the compile time issue would be worse on older hardware and CI.

strager··on A practical comparison of build and test speed between C++ and Rust
> I wonder if OP's C++ port doesn't use iterators that much, and how idiomatic it is.

I think I only used iterators in places where there's no built-in function on slices like C++'s strchr and strspn. (I think Rust's str has these, but not [u8].) For example:

C++: https://github.com/quick-lint/cpp-vs-rust/blob/f8d31341f5cac...

    std::size_t length = std::strcspn(c, separators);
    if (c[length] == '\0') {
      return found_separator{.length = length,
                             .which_separator = static_cast<std::size_t>(-1)};
    }
    const char* separator = std::strchr(separators, c[length]);
Rust: https://github.com/quick-lint/cpp-vs-rust/blob/f8d31341f5cac...

    match s
        .as_bytes()
        .iter()
        .position(|c: &u8| separators.contains(c))
    {
        None => FoundSeparator {
            length: s.len(),
            which_separator: INVALID_WHICH_SEPARATOR,
        },
        Some(length) => {
            let found_separator: u8 = unsafe { *s.as_bytes().get_unchecked(length) };
            match separators.iter().position(|c: &u8| *c == found_separator) {
strager··on A practical comparison of build and test speed between C++ and Rust
> the default automated Rust formating tool is very eager to adds lot of lines by basically keeping only one word per line.

This is not my experience.

Lifetime and '&mut self' noise (and four-space indentation) did cause rustfmt to sometimes split function signatures across multiple lines, but overall, I think rustfmt did a good job.

C++: https://github.com/quick-lint/cpp-vs-rust/blob/f8d31341f5cac...

    lexer::parsed_identifier lexer::parse_identifier(const char8* input,
                                                     identifier_kind kind) {
      const char8* begin = input;
      const char8* end = this->parse_identifier_fast_only(input);
      if (*end == u8'\\' || (kind == identifier_kind::jsx && *end == u8'-') ||
          !this->is_ascii_character(*end)) {
        return this->parse_identifier_slow(end,
                                           /*identifier_begin=*/begin, kind);
      } else {
        return parsed_identifier{
            .after = end,
            .normalized = make_string_view(begin, end),
            .escape_sequences = {},
        };
      }
    }

Rust: https://github.com/quick-lint/cpp-vs-rust/blob/f8d31341f5cac...

    fn parse_identifier(
        &mut self,
        input: *const u8,
        kind: IdentifierKind,
    ) -> ParsedIdentifier<'alloc, 'code> {
        let begin: *const u8 = input;
        let end: *const u8 = self.parse_identifier_fast_only(input);
        let end_c: u8 = unsafe { *end };
        if end_c == b'\\'
            || (kind == IdentifierKind::JSX && end_c == b'-')
            || !is_ascii_code_unit(end_c)
        {
            self.parse_identifier_slow(end, /*identifier_begin=*/ begin, kind)
        } else {
            ParsedIdentifier {
                after: end,
                normalized: unsafe { slice_from_begin_end(begin, end) },
                escape_sequences: None,
            }
        }
    }
strager··on A practical comparison of build and test speed between C++ and Rust
> If I was thinking about how to get fast builds it would force the language to change - header files would be banned and that means macros and the whole idea of preprocessing. That means a different approach to multi-platform support.

I think this is what Rust does. No header files; #[cfg] syntax for conditional compilation for either source files or sections of code. But it doesn't seem to fundamentally help build times. xD (But maybe the bottlenecks in Rust are elsewhere.)

strager··on A practical comparison of build and test speed between C++ and Rust
> I think it's hard to draw absolute conclusions because small things can make a huge difference to build times.

Of course. I tried my best to optimize the C++ build and the Rust build.

> It's also very important to be able to see how your build tool is scheduling tasks and making use of your CPUs - often some odd dependencies force most of your cores to sit idle while something gets done.

This is a good point. I didn't spend much time profiling and tweaking this aspect of the Rust build (cargo build --timings).

> The structure of your code matters too - a simple optimisation for C/C++ is just to have much larger source files rather than splitting everything up into many small ones. This acts to reduce the number of repeated includes.

For clean builds, yes, this helps. But for incremental builds, where I need to test a change to just one .cpp file, larger source files are worse.

strager··on A practical comparison of build and test speed between C++ and Rust
Were those proc macros that you wrote, or proc macros in third-party crates?
strager··on A practical comparison of build and test speed between C++ and Rust
> Sanitizers should primarily impact runtime performance anyways, but that's moot.

In my experience, ASAN has a huge negative impact on build times for C++ code (Clang and GCC).

strager··on A practical comparison of build and test speed between C++ and Rust
Oh, I did not know this.
strager··on A practical comparison of build and test speed between C++ and Rust
The article does not account for this. I wanted the fastest build-test cycle. It sounds unfair to artificially slow down C++ compilation just because Rust has undisablable checks.

EDIT: Oops, I realized I already replied two hours ago.

strager··on A practical comparison of build and test speed between C++ and Rust
> This is something Rust folks should strive to solve as a differentiator, because there is no realistic way of making incremental C++ builds faster.

I think C++20 modules are the promised golden ticket. It's 2023 and I still can't use them on Linux, though.

strager··on A practical comparison of build and test speed between C++ and Rust
I also benchmarked the Rust code with a monolithic crate. It built slower. https://quick-lint-js.com/blog/cpp-vs-rust-build-times/#opti...
strager··on A practical comparison of build and test speed between C++ and Rust
> An interesting thought is that we really ought to not compare rust with just GCC, but GCC + clang-tidy

Nitpick: The article compares Clang, not GCC. (GCC is mentioned only to show how much faster Clang is.)

> you get so much more static analysis by default in rust compilation than C++.

I care about the build-test cycle, so adding static analysis is against my goals.

I would minimize the analyses done by rustc if I could.

strager··on A practical comparison of build and test speed between C++ and Rust
I'm sorry to hear that!

Did you try upgrading your hardware? It really helps.

strager··on A practical comparison of build and test speed between C++ and Rust
Good question!

Based on my profiling, it seems like the code gen backend (LLVM) is a big offender. A code generator optimized for build times would be nice. (Cranelift seems to be a failure in that regard.)

C++ has an explicit template instantiation feature which helps reduce backend time. I wonder if this can be done in Rust too. Maybe this is what -Zshare-generics=y is for?

strager··on A practical comparison of build and test speed between C++ and Rust
I checked how long linking takes for quick-lint-js [1] with Mold. 108 milliseconds. That's pretty chunky. I should try .so files like you suggest.

[1] the full project, not the 17k SLOC subset discussed in the article

strager··on A practical comparison of build and test speed between C++ and Rust
Yup! And I evaluated some different crate structures: https://quick-lint-js.com/blog/cpp-vs-rust-build-times/#opti...
strager··on A practical comparison of build and test speed between C++ and Rust
> I don’t think the Rust code could be shorter than the C++ code when converting pretty much line by line, since that leads to C++-style Rust code.

I thought the opposite: header files (and CMakeLists.txt, which was included in the subtotal SLOC) artificially increase C++ SLOC.

> For example idiomatic Rust code can do with less defensive programming due to language features.

Can you show an example in the Rust code where this might be possible? https://github.com/quick-lint/cpp-vs-rust/tree/master/rust

strager··on A practical comparison of build and test speed between C++ and Rust
> Of course Rust is slower than C++. C++ has had decades of maturity and has enjoyed many contributors to its tooling. Rust has not, and its feature set (as in, the things the compiler needs to do) is much broader than any C++ compiler is specified to do.

My thinking was the opposite. Because Rust is newer, it doesn't have the bloaty baggage that C++ and its compilers have. And Rust's module system is (in theory) far better for build times than C++'s #include-based module system.

But instead of speculating, I put the time in to figure out the answer.

strager··on A practical comparison of build and test speed between C++ and Rust
> I don't really like these comparisons looking at these languages through the lens of commonality. The only thing the two have in common is that they're systems languages. > > [...] In my opinion, anyone who argued about which language is better than which other language has completely missed the point of computer science and programming.

When I start a new programming project, I need to choose a language. For this project, three years ago, I had three serious choices:

* C * C++ * Rust

I rejected C because I wanted the productivity boost from C++ or Rust. I rejected Rust because Rust was new to me (thus risky) but I was very familiar with C++.

I don't see why it's a bad idea to compare languages—especially along specific dimensions, like run-time perf or build time or safety—when that's what us software engineers do whenever we start a new project.

strager··on A practical comparison of build and test speed between C++ and Rust
That's a great idea! Unfortunately, I think there's no way to disable rustc's borrow checking or other safety features.
strager··on A practical comparison of build and test speed between C++ and Rust
> not every C++ project is full of template metaprogramming

You're right. My project is not one of those projects.

As mentioned in the article, the C++ project has no dependencies (outside googletest and the standard library, whose build time isn't included in the benchmarks).

Also, the project is relatively light on templates compared to most C++ code that I've seen. The biggest offenders are narrow_cast and vector.

> Also at least in VC++, modules are already a thing

I'd love to use them, but I mostly work on Linux and macOS. =[

> Still looking forward to binary libraries support in cargo (well one can dream).

I didn't consider this downside of Rust! I imagine rlibs can be cached manually, right? I don't know if rlibs are portable between machines.

strager··on A practical comparison of build and test speed between C++ and Rust
No.

If I was going to run sanitizers with the C++ code, for a fair comparison, I would also run the Rust code with Miri. (C++ sanitizers check unsafe code, but without Miri, rustc mostly checks only safe code.)

strager··on The number of comparisons needed to sort a shuffled array: qsort vs. std:sort
That would imply that the input array given to qsort was misaligned. I don't think alignment is an issue in the comparator.
strager··on Improving Firefox Responsiveness on macOS
This happens for me with YouTube in Safari occasionally. It might be a bug with the website, not Firefox or Safari.
strager··on LAPD Officer Killed During Training Exercise Was Investigating Cops About Rape
> Are there not corrupt politicians?

There are. And democracy makes politicians seem more legitimate. Democracy in the wrong place can make corruption worse.

Hiring cops should not be a popularity contest (i.e. democratic) in my opinion.

← PreviousPage 2 of 7Next →