News for hackers. Stuff that matters.
Re:Making Rust as Fast as Go by matklad on Sunday May 03, 2020 @10:00AM (#23059032)
Note that Rust and Go programs differ in a seemingly insignificant, but actually important detail.

Rust does this

    next_dist = std::cmp::min(
        dist_if_substitute,
        std::cmp::min(dist_if_insert, dist_if_delete),
    );
Go does this

    nextDist = min(
         distIfDelete, 
         min(distIfInsert, distIfSubstitute)
    )
The order of minimums is important for this dynamic programming loop. If I change Rust version to take minimums in the same order (swapping substitute and delete), runtime drops from 1.878696288 to 1.579639363.

I haven't investigated this, but I would guess that this is the same effect I've observed in

* https://matklad.github.io/2017/03/12/min-of-three.html

* https://matklad.github.io/2017/03/18/min-of-three-part-2.htm...

(reposting my comment from reddit, as it's a rather unexpected observation)

Re:Making Rust as Fast as Go by ttd on Sunday May 03, 2020 @11:58AM (#23059789)
Those are two really well written articles -- taking a complicated topic and making it very accessible. Thanks! FWIW I think a companion article on how to effectively use perf for tasks like this would be a great addition, since it can be a bit novice-unfriendly.
Re:Making Rust as Fast as Go by ishanjain28 on Sunday May 03, 2020 @12:28PM (#23059997)
I think they made the rust version same as Go because I cloned it just now and they are both the same. Also, Thank you soo much for the blog posts! :)
Re:Making Rust as Fast as Go by _____smurf_____ on Sunday May 03, 2020 @03:59PM (#23061420)
Given this information, and for general parsing functionalities, which one is faster, Go or Rust?
Re:Making Rust as Fast as Go by arcticbull on Sunday May 03, 2020 @04:28PM (#23061623)
As always, it depends on what your goals are. String processing is usually not the long pole when you're building something that consumes the output of a parser. Based on micro and macro benchmarks, Rust is typically substantially faster than Go and pretty much always uses less RAM [1].

But again, depends what you're doing with the output, and if these deltas even matter in your context.

[1] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

Re:Making Rust as Fast as Go by fluffything on Sunday May 03, 2020 @04:18PM (#23061556)
You can write a parser as far as the limits of your imagination allow in Rust, or C, or any other baremetal language without a thick run-time.

In Go, beyond the limits of your imagination, you'll also hit other limits, like those of the garbage collector.

Re:Making Rust as Fast as Go by drfuchs on Sunday May 03, 2020 @02:22PM (#23060861)
Very few things have ever been measured accurately to ten significant digits. How much did these numbers change run to run? How many measurements did you take? Were the caches warmed up similarly? Still, point taken.
Re:Making Rust as Fast as Go by matklad on Sunday May 03, 2020 @02:51PM (#23061025)
The excessive "precision" is because I've just copy-pasted what the original bench printed.

As for "is this a reliable result", I believe I've performed diligence, appropriate for a HN comment, to make sure that this is not a completely random result. As I've said, I did not investigate this particular bit of code thoroughly. You are welcome to read the linked blog posts, which study the issue in depth.

Re:Making Rust as Fast as Go by londons_explore on Sunday May 03, 2020 @02:30PM (#23060908)
Since min(a, min(b,c)) == min(b, min(a,c)), perhaps the compiler should be smart enough to swap the comparisons around if it makes it quicker?
Re:Making Rust as Fast as Go by dan-robertson on Sunday May 03, 2020 @02:48PM (#23061000)
I suspect that statement is not true for floats. Possibly you don’t get the same float from min(0,-0) as min(-0,0), and similarly with NaNs. Rust specifies that if one input is NaN then the other is returned but doesn’t say what happens if both are NaN.
Re:Making Rust as Fast as Go by fluffything on Sunday May 03, 2020 @04:21PM (#23061569)
> Rust specifies that if one input is NaN then the other is returned but doesn’t say what happens if both are NaN.

It does. If both are NaN a NaN is returned. Note, however, that when Rust says that a NaN is returned, this means that any NaN can be returned. So if you have min(NaN0, NaN0) the result isn't necessarily NaN0, it might be another NaN with a different payload.

Re:Making Rust as Fast as Go by dan-robertson on Sunday May 03, 2020 @06:13PM (#23062290)
Right. That’s what I was getting at but I didn’t know the NaN-return rule.
Re:Making Rust as Fast as Go by gpm on Sunday May 03, 2020 @02:51PM (#23061031)
Hmm, not sure what the llvm semantics look like, but you're right about the assembly semantics, vminsd (the instruction used in the post) is unfortunately not symettric in it's handling on NaN.

https://www.felixcloutier.com/x86/minsd

Re:Making Rust as Fast as Go by londons_explore on Sunday May 03, 2020 @04:11PM (#23061500)
NaN's are rarely required on the fast path of any code... The compiler ought to make a 2nd slow implementation for if any NaNs are seen.
Re:Making Rust as Fast as Go by fluffything on Sunday May 03, 2020 @04:22PM (#23061575)
Sure, but then it needs to add a branch to detect the inputs, and maybe keep around some state to switch between implementations at run-time.

Its far from clear that doing that is, in general, worth it.

Re:Making Rust as Fast as Go by arcticbull on Sunday May 03, 2020 @02:51PM (#23061026)
Part of the problem may be they re-implemented `std::cmp::min` at the bottom of the file, I wonder if there's a more optimized version in the stdlib.
Re:Making Rust as Fast as Go by gpm on Sunday May 03, 2020 @02:53PM (#23061050)
One of the most frustrating parts of rust (for me), is that `std::cmp::min` (and some other methods) require that their arguments are `Ord` (totally ordered), and floats are only partially order because of NaN, so you can't use std::cmp::min on floats.
Re:Making Rust as Fast as Go by shepmaster on Sunday May 03, 2020 @06:54PM (#23062564)
Re:Making Rust as Fast as Go by arcticbull on Sunday May 03, 2020 @03:31PM (#23061240)
I do believe that's quite intentional, due to the inexactness of floating-point values, it's not "right" to do that. There should be a `partial_min` function though, IMO, which has weaker guarantees.
Re:Making Rust as Fast as Go by gpm on Sunday May 03, 2020 @03:33PM (#23061250)
It's absolutely intentional, and it's got being technically correct on its side, it's just frustrating.
Re:Making Rust as Fast as Go by oxnrtr on Monday May 04, 2020 @07:48AM (#23066853)
Not really. That Rust forced total order and partial order into a sub-typing relation is a completely unnecessary, self-inflicted wound.
Re:Making Rust as Fast as Go by steveklabnik on Monday May 04, 2020 @04:57PM (#23073158)
Many folks do consider this a mistake. Some do still think it was the right call. I'd say it's very unclear.
Re:Making Rust as Fast as Go by Arnavion on Sunday May 03, 2020 @05:17PM (#23061918)
You can use the ordered_float crate. It has a newtype that cannot hold NaNs and thus impls Ord.
Re:Making Rust as Fast as Go by steveklabnik on Sunday May 03, 2020 @03:28PM (#23061220)
Depending on what behavior you want, there’s a bunch of wrapper crates.
Re:Making Rust as Fast as Go by jfkebwjsbx on Sunday May 03, 2020 @05:22PM (#23061960)
Yeah, it makes no sense to be the default. Code that expects to treat NaNs is very rare.
Re:Making Rust as Fast as Go by arcticbull on Sunday May 03, 2020 @06:46PM (#23062518)
Safety is the whole idea behind Rust, and if you draw the line here, that's neither in line with the Rust ethos, nor particularly valuable. After all, code that "expects to handle" null was pretty rare too ;)
Re:Making Rust as Fast as Go by jfkebwjsbx on Sunday May 03, 2020 @07:40PM (#23062821)
NaNs have nothing to do with safety (same for nulls).

It is a common misconception to conflate safety with functional expectations. A program that only calls panic() is perfectly safe.

Re:Making Rust as Fast as Go by arcticbull on Sunday May 03, 2020 @07:44PM (#23062850)
Ah sorry, I didn't mean safety in the UB sense, I meant in the traditional, "do what I expect, don't surprise me" sense.
Re:Making Rust as Fast as Go by jfkebwjsbx on Sunday May 03, 2020 @08:53PM (#23063322)
Safety in Rust's context is UB-free, memory errors-free, data race-free.

Safety in software engineering is more about designing systems with some degree of assurance against certain failures, but not about surprises or expectations of a programmer.

The usage you call traditional is perhaps common, but not really rooted in anything in software engineering. I'd call it an informal meaning, maybe.

PS. No need for apologies!

Re:Making Rust as Fast as Go by gpm on Sunday May 03, 2020 @09:42PM (#23063706)
Safety as in unsafe blocks means what you say. But safety as in why so many people use rust is actually what arcticbull says. I think this is something that gets lost on a lot of people who don't use rust. The first form of safety is properly called memory safety.

Rust as a language isn't just designed to avoid undefined behavior, it's designed to make you write correct code, where correct means it does what you want it to. Obviously rust doesn't always succeed at that broader goal, but it actually does a pretty good job all things considered.

Arcticbull's description of rust's ethos is spot on.