Hacker News new | past | comments | ask | show | jobs | submit login

From my understanding, Rust performs more compile time checks for safety. In those cases, you may be able to maintain the performance of C without sacrificing safety. On the other hand, you may still get away with writing more performant C code since C allows the developer to play fast and loose with the rules. I'm not saying that is a good thing, but git can get away with it since it since it probably attracts very competent developers.

The other thing mentioned in the post was eliminating overhead. My limited experience with Rust leads me to believe it is closer to C++ than C in this respect. That is to say there is a lot of higher level functionality that you can use and it is optimized for performance in the general case. On the other hand, that functionality is still for the general case, so a competent developer who has a thorough knowledge of what the program is trying to accomplish will be able to write more performant code.




That C allows developers to play fast and loose is actually one of the reasons why C is slower than Fortran in many cases. Aliasing does not exist in Fortran, and array dimensions are well-defined, and this alone allows for much more aggressive optimizations, impossible to do safely in C.

(Is Fortran still relevant? Try compiling Tensorflow, to say nothing of serious numerical modeling stuff. And yes, I know about __restrict; it's more of a band-aid.)


The version of Fortran I worked with had no stack nor heap. Needless to say, it was very fast.


> Aliasing does not exist in Fortran

Ha, I have some disgusting use of COMMON blocks from the 80's to show you. But that doesn't affect the optimizer's ability to assume that there's no aliasing, so you're totally correct about that.


> C allows the developer to play fast and loose with the rules.

So does Rust, in unsafe blocks. Is there anything that can be done in C that can’t be done in unsafe Rust?


There are some architectures that C supports better than Rust due to having gcc's backends as an option as well as LLVM, but within a given architecture that both support, I don't think there is.


Only thing that comes to mind immediately is VLA/alloca but I'd have to check. Most of the gnarly pointer things are fair game and work just as well as their C counterparts.


VLA are thankfully no longer required for ISO C compliance, who had thought it was a good idea in first place?

Google even sponsored the work to clean the Linux kernel from them.


Rust supports VLAs, Box<[T]> is pretty common in fact. It just doesn’t have a native way to alloc them on the stack.


> Is there anything that can be done in C that can’t be done in unsafe Rust?

Duff's device <https://en.wikipedia.org/wiki/Duff's_device>.


You're mixing up a thing you can do with a way that you can do something. You can write Rust code with the same observable behavior as Duff's device in C.


Also, Duff’s device is pretty terrible and a modern compiler will output much better code.


> You're mixing up a thing you can do with a way that you can do something.

I don't think they are.

> You can write Rust code with the same observable behavior as Duff's device in C.

Yes, turing complete languages can do that.


A good example of cool tricks to impress fellow hackers that have no place in code bases that should meet certain quality levels of long term maintenance.


Modifying memory on the heap in multiple threads without a mutex, from one pointer.

Why anyone would want that on purpose, I don't know.


What stops you from doing this in Rust?


The language itself, unless you go out of your way to circumvent the protections with `unsafe`.

In this particular case it's the `Sync` trait and the generell rules around sharing data.


> unless you go out of your way to circumvent the protections with `unsafe`

I was specifically referring to the things you can do in `unsafe` blocks.


It was my impression that you can't do it even in unsafe because ownership rules still apply. But apparently I was wrong and there is a way to do it?


Ownership rules still apply _to references_, but not to raw pointers.

This program prints "12". The only difficulty is that Rust's designers have intentionally made this pattern difficult by marking raw pointers as !Send, but you can get around that by wrapping them in a Send type.

    use std::{time::Duration, ops::{DerefMut, Deref}};
    
    struct SendWrapper<T>(*mut T);
    
    impl<T> Deref for SendWrapper<T> {
        type Target = T;
    
        fn deref(&self) -> &Self::Target {
            unsafe { self.0.as_ref() }.unwrap()
        }
    }
    impl<T> DerefMut for SendWrapper<T> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            unsafe { self.0.as_mut() }.unwrap()
        }
    }
    
    unsafe impl<T> Send for SendWrapper<T> {}
    
    fn main() {
        let mut x = 10;
        let p1: *mut usize = &mut x;
        let p2: *mut usize = &mut x;
        let mut s1 = SendWrapper(p1);
        let mut s2 = SendWrapper(p2);
    
        let j1 = std::thread::spawn(move || {
            *s1.deref_mut() += 1;
        });
        let j2 = std::thread::spawn(move ||  {
            std::thread::sleep(Duration::from_secs(1));
            *s2.deref_mut() += 1;
        });
        j1.join().unwrap();
        j2.join().unwrap();
        println!("x: {}", x);
    }


There are many benefits. Here is a good video that explains:

https://youtu.be/c1gO9aB9nbs


Rust does have UnsafeCell for gurus/experts to build such complex concurrency building blocks, pack it in a safe API for us plebs to use.


> On the other hand, you may still get away with writing more performant C code since C allows the developer to play fast and loose with the rules.

I have some thoughts on how Rust performance compares with C!

I spent yesterday optimizing a sort routine in Rust. I need to sort records where the key length is known at compile-time, and the payload length is different every time sort is called. I expect the average sort call to involve at least 250 million rows and over 400 columns. Speed matters.

Here's what I've learned so far, about how Rust versus C question:

- Rust has generics with monomorphization. This makes it easy to compile two different versions of the sort routine to run on different length keys.

- The very fastest version of my recursive base case, a 20-item insertion sort, currently uses unsafe code. This allows me to eliminate a couple of bounds checks that LLVM isn't eliminating automatically. I may figure out how to do this with safe code before shipping. (In 5+ years of production Rust, I've never needed unsafe for performance. This may be the first time!)

- Performance often comes down to cache locality. Both C and Rust give me the fine-grained memory layout control that's essential. Also, this means that "detached key" sorts are almost always a bad choice, even when the alternative is repeatly moving large record payloads around.

- The Rust "criterion" benchmarking library makes it super-easy to run valid performance tests.

- The Rust "proptest" allows me to generate large amounts of test data and verify properties like "reckless_sort always produces output matching std sort."

- "cargo fuzz" will be useful when I try to break the finished code. Seriously, it's a super-nice fuzzer workflow.

- Rust's standard quicksort contains all sorts of funky optimizations: It detects semi-sorted arrays, it breaks patterns, it falls back to merge sort when things go wrong. It even marks "cold" paths for LLVM to improve instruction cache usage.

- If I get my sort working, the "rayon" library will make it utterly painless and safe to split up the recursive calls over multiple CPUs.

Overall, this has been a pretty fun experience. I'm not sure whether the finished product will use unsafe Rust in the inner loop. But the combination of generics, rayon, and tightly integrated test/bench/fuzz tooling has made this a very pleasant experience.

Verdict: I am entirely happy using Rust for hot loops. (Even if my current fastest inner loop does use "unsafe" to bypass a few bounds checks I'm not clever enough to eliminate otherwise.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: