Rayon is definitely the best parallelism library I've ever used. We recently switched Servo over to using it for parallel restyling and layout and saw small gains in performance over our previous solution and drastic reduction in code complexity (and removed a whole pile of domain-specific unsafe code).
Being able to switch .iter() to .par_iter() and have things "just work" is a game changer.
The crucial thing about rayon is that sequential fallback is really fast, almost as fast as the sequential code you'd write anyway. This is important because, as paradoxical as it sounds, most CPU-bound programs work with small workloads most of the time, and so they don't want the overhead of parallelism for those cases. (It's the analogue of saving power by putting the CPU to sleep when it's not in use.) The occasional big workload that comes along is what you really want parallelism for, and the big trick is to handle that case without regressing the common sequential case. Rayon's work stealing approach based around scoped iterators is the ideal solution for this.
I have played around a tiny bit with par_iter over blocking IO tasks and seen some sched_yield() loops burning CPU time instead of backing off to futex_wait. That seems suboptimal and not exactly "the best ever" I'd expect from a parallelism library.
> Rayon is definitely the best parallelism library I've ever used.
Whenever people say this, I ask the following question in order to gauge whether I want to try the library in question:
Have you used Twisted?
Twisted is, to me, the quintessential example of a high-quality open source project. If you have used it extensively and still recommend Rayon, I'll give it a try.
This is very nice. In Rust, if you accidentally share mutable data between threads, the borrow checker should catch it at compile time. Few other languages catch such errors. Go, for example, does not. This makes writing parallel code much, much safer.
I have not done any real programming in Rust, but whenever I see Rust code I'm amazed how different is it from Go, despite both having some shared use cases. Go's main selling point beyond concurrency is simplicity. And it's the simplicity that I like about it. On the other hand, it looks to me like Rust is turning into Scala.
My personal experience with Rust (vs. Go and other languages) is that there is something really magical that all the sigils and syntactic complexity give you: once you've internalized Rust's approach it's ridiculously easy to build an accurate mental model of what's happening in almost any piece of code. From the high level constructs down to generated assembly, Rust produces the most predictable code I've ever written. While not the same kind of simplicity you're describing, my experience is that the value of Go's simplicity is to give you a low overhead mental model. While Rust has some complexity, I personally find it to have the lowest overhead mental model of anything I've worked with, due to its predictability, explicitness, and strong conventions.
Granted, it definitely took some time for me to gain the experience necessary for this magic to occur. It's not really a "hack a quick thing together once every 10 years" kind of language.
It's hard to convey this without getting you to actually learn Rust for a few days/weeks/months, but it's certainly been my experience and I hear it all the time from other Rust developers.
I know scala a bit and rust only barely, but they seem like very different languages to me.
Scala tries to enable you to create whatever api you want, so it has many different flavours of magic to allow flexibility of expression. Predictably this is used and abused horribly by a community who can't come to a consensus on what good taste is.
Rust on the other hand makes you explicitly say everything that happens. Nothing will happen without you being aware of it, and that makes it wordy and feel more complex, but it's the kind of complexity of having to say everything you mean down to a much greater level of detail rather than the kind of complexity that arises when it's almost impossible to know exactly what is going on.
Rust and Scala are pretty much at opposite ends of the 'magic' spectrum.
Are there really a lot of "shared use cases" between Rust and Go?
I realize that Go's creators intended for it to be a systems language. In practice though, it has found its niche in web apps or microservices, and command-line apps in the DevOps world. Rust is primarily aimed toward real-time applications that can't tolerate garbage collection latency.
Of course they're both general-purpose languages in theory. But in the real world, one is really competing with Java and Python while the other is competing with C++. I don't even see Go and Rust as head-to-head competitors at all... and I definitely don't understand their uni-directional "feud" (i.e. nearly every Rust thread is has people taking shots at Go, yet most Go threads don't mention Rust at all).
What do you mean by "turning into"? I don't see much special syntax in the post apart from the general expression-oriented functional style, including higher-order functions. Which has always been part of Rust. If that means turning into Scala, then almost every high-level programming language is currently turning into Scala.
From the starting point of being a professional Java developer and a hobbyist Haskeller, Scala should've been the perfect language for me, yet I struggle with making it past just how complicated the language is -- not really complex, as such, just over-engineered.
To me, Rust feels like the simplest possible language that could achieve their design goals, whereas Go feels like they went so far out of their way to make it simple that they lost track of the other goals altogether.
Because I'm one of the only people passionate about Rust at my job (and there are plenty who love Go), I'm often asked questions about Rust vs. Go, which actually perplexes me a little. Asking "What about Go?" when someone brings up Rust is essentially like saying "What about OCaml?"; the languages have a few similar features (as most languages do, IMO), but they have completely different goals and only a subset of shared use-cases (and again, you'll likely find at least some shared use-cases if you pick any two languages). I feel like constantly bringing up Rust and Go together confuses things more than it helps, since they really aren't that similar.
If one were to have experience in both Rust and Scala, they would see that the comparison between the two is flawed. Scala revels in implicitness, whereas Rust is very explicit. Scala also has expressiveness as a goal, whereas in Rust expressiveness is an anti-goal. There are no user-defined operators in Rust, and less magic than in any language that I've used other than C.
My main problem with rust at this point is the noise caused by a lot of the wrangling of standard types.
I know it's a low-ish level language that leaves a lot explicit for the developer to type, and that a lot of boilerplate things could yet be turned into conventions or macros much like try!, as the language matures.
I'd really like to see this process accelerate as a lot of Rust code now is littered with into(), unwrap() etc. which causes mental overhead for the reader.
I'd like to see more "zero cost abstractions" where the zero also includes zero characters typed that aren't part of the problem itself. As a benchmark, I'd like to see "noise parity" with go/ocaml/swift while still having the nice safety/performance Rust has today.
Closures in Rust are stack-allocated and LLVM can inline them and optimize them as if they're any regular imperative code, for one thing. This means that there's less overhead from managing the iterator chains, and that they're statically dispatched which saves on runtime indirection. The borrow checker also makes sure that you don't accidentally mutate non-thread-safe data from your parallel iterators.
[+] [-] pcwalton|9 years ago|reply
Being able to switch .iter() to .par_iter() and have things "just work" is a game changer.
The crucial thing about rayon is that sequential fallback is really fast, almost as fast as the sequential code you'd write anyway. This is important because, as paradoxical as it sounds, most CPU-bound programs work with small workloads most of the time, and so they don't want the overhead of parallelism for those cases. (It's the analogue of saving power by putting the CPU to sleep when it's not in use.) The occasional big workload that comes along is what you really want parallelism for, and the big trick is to handle that case without regressing the common sequential case. Rayon's work stealing approach based around scoped iterators is the ideal solution for this.
[+] [-] p0nce|9 years ago|reply
It's called .parallel() in D, works the same way I guess. It turns a lazy computation chain into a parallel one.
[+] [-] the8472|9 years ago|reply
[+] [-] unknown|9 years ago|reply
[deleted]
[+] [-] jMyles|9 years ago|reply
Whenever people say this, I ask the following question in order to gauge whether I want to try the library in question:
Have you used Twisted?
Twisted is, to me, the quintessential example of a high-quality open source project. If you have used it extensively and still recommend Rayon, I'll give it a try.
[+] [-] Animats|9 years ago|reply
[+] [-] lukaslalinsky|9 years ago|reply
[+] [-] anp|9 years ago|reply
Granted, it definitely took some time for me to gain the experience necessary for this magic to occur. It's not really a "hack a quick thing together once every 10 years" kind of language.
It's hard to convey this without getting you to actually learn Rust for a few days/weeks/months, but it's certainly been my experience and I hear it all the time from other Rust developers.
[+] [-] kybernetikos|9 years ago|reply
Scala tries to enable you to create whatever api you want, so it has many different flavours of magic to allow flexibility of expression. Predictably this is used and abused horribly by a community who can't come to a consensus on what good taste is.
Rust on the other hand makes you explicitly say everything that happens. Nothing will happen without you being aware of it, and that makes it wordy and feel more complex, but it's the kind of complexity of having to say everything you mean down to a much greater level of detail rather than the kind of complexity that arises when it's almost impossible to know exactly what is going on.
Rust and Scala are pretty much at opposite ends of the 'magic' spectrum.
[+] [-] StevePerkins|9 years ago|reply
I realize that Go's creators intended for it to be a systems language. In practice though, it has found its niche in web apps or microservices, and command-line apps in the DevOps world. Rust is primarily aimed toward real-time applications that can't tolerate garbage collection latency.
Of course they're both general-purpose languages in theory. But in the real world, one is really competing with Java and Python while the other is competing with C++. I don't even see Go and Rust as head-to-head competitors at all... and I definitely don't understand their uni-directional "feud" (i.e. nearly every Rust thread is has people taking shots at Go, yet most Go threads don't mention Rust at all).
[+] [-] derkha|9 years ago|reply
[+] [-] pdpi|9 years ago|reply
To me, Rust feels like the simplest possible language that could achieve their design goals, whereas Go feels like they went so far out of their way to make it simple that they lost track of the other goals altogether.
[+] [-] saghm|9 years ago|reply
[+] [-] kibwen|9 years ago|reply
[+] [-] pcwalton|9 years ago|reply
Can you describe the simpler signature of par_iter() that would work in Go?
Can you describe the Go features that allow the compiler to prevent data races at compile time?
[+] [-] alkonaut|9 years ago|reply
I know it's a low-ish level language that leaves a lot explicit for the developer to type, and that a lot of boilerplate things could yet be turned into conventions or macros much like try!, as the language matures.
I'd really like to see this process accelerate as a lot of Rust code now is littered with into(), unwrap() etc. which causes mental overhead for the reader.
I'd like to see more "zero cost abstractions" where the zero also includes zero characters typed that aren't part of the problem itself. As a benchmark, I'd like to see "noise parity" with go/ocaml/swift while still having the nice safety/performance Rust has today.
[+] [-] unknown|9 years ago|reply
[deleted]
[+] [-] ceronman|9 years ago|reply
[+] [-] dikaiosune|9 years ago|reply
[+] [-] pcwalton|9 years ago|reply