Making Rust as Fast as Go
christianfscott.com
Making Rust as Fast as Go
1–10 of 211 posts
Re: Making Rust as Fast as Go
#2Re: Making Rust as Fast as Go
#3I am confused by the implementations, although I have not spent any time testing them. Both versions contain a mix of code that counts bytes (`.len()` and `len(...)`) and Unicode code points (`chars()` and `[]rune(...)`). My guess is that the implementation might not work correctly for certain non-ASCII strings, but I have not verified this.
Of course, if only ASCII strings are valid as input for this implementation then both versions will be a lot faster if they exclusively operate on bytes instead.
Re: Making Rust as Fast as Go
#4The speed difference came from the allocator.
Rust switched from jemalloc to the system allocator per ticket #36963[0] for various reasons (like binary bloat, valgrind incompatibility, etc...).
Go uses a custom allocator[1] instead.
To make 'Rust Go fast' (pun intended), one can use the '#[global_allocator]' to use a custom allocator (in this case, with the jemallocator crate) to make allocations fast again.
Re: Making Rust as Fast as Go
#5My understanding is that Go doesn’t use the libc at all and makes system calls directly, which IMO is the correct decision in a modern systems programming language that doesn’t want to be limited by 40 years of cruft.
Re: Making Rust as Fast as Go
#6edit: this has been pointed out as incorrect, Go ints are 8 bytes on 64bit systems -- thanks for the correction!
let mut cache: Vec = (0..=target.chars().count()).collect();
which can be simplified as let mut cache: Vec = vec![0; target.len()];
vs cache := make([]int, len(target)+1)
for i := 0; i
Rust usize being 8 bytes and Go int being 4 bytes as I understand it.So between doing more work and worse cache usage, it wouldn't be surprising if the Rust version was slower even with the faster allocator.
Re: Making Rust as Fast as Go
#7The Rust version uses `target.chars().count()` to initialise the cache, while the Go version counts up to `len(target)`. These are not equivalent: the Rust version counts Unicode code points, the Go version counts bytes. I am confused by the implementations, although I have not spent any time testing them. Both versions contain a mix of code that counts bytes (`.len()` and `len(...)`) and Unicode code points (`chars(…
I was a bit suspicious of the conclusion, but didn’t dig in myself. I imagine this would be a much larger source of difference.
Re: Making Rust as Fast as Go
#8The Rust version uses `target.chars().count()` to initialise the cache, while the Go version counts up to `len(target)`. These are not equivalent: the Rust version counts Unicode code points, the Go version counts bytes. I am confused by the implementations, although I have not spent any time testing them. Both versions contain a mix of code that counts bytes (`.len()` and `len(...)`) and Unicode code points (`chars(…
Here a Go playground example showing that the result is indeed wrong:
https://play.golang.org/p/vmctMFUevPc
It should output 3 but outputs 5 because each ö is two bytes, len("föö") = 5.
I would suggest using "range" to iterate over the unicode characters.
Re: Making Rust as Fast as Go
#9Re: Making Rust as Fast as Go
#10Assuming this was run on a 64bit system, the Rust version seems to be allocating and zeroing twice as much memory as the Go version. edit: this has been pointed out as incorrect, Go ints are 8 bytes on 64bit systems -- thanks for the correction! let mut cache: Vec = (0..=target.chars().count()).collect(); which can be simplified as let mut cache: Vec = vec![0; target.len()]; vs cache := make([]int, len(target)+1) for…