5 ms·
I've become parallelism optimist thanks to Rust. The borrow checker basically doesn't even have a concept of a single-threaded program. Everything you write in
by pornel 8y ago
I've become parallelism optimist thanks to Rust.
The borrow checker basically doesn't even have a concept of a single-threaded program. Everything you write in Rust is required to be thread-safe. This makes Rust appear more difficult, but it's just front-loading the effort required to make the whole program thread-safe (which to me felt nearly impossible for non-trivial C programs). From there you can sprinkle Rayon all over the place, send async work over channels, etc.
The piece I'm missing is "Amdahl's-law-aware" profiler that would show not just where any code spends time, but only where the unparallelized bottlenecks spend time.
- twotwotwo 8y agoRe: showing bottlenecks, the Go latency tracer may be a neat reference point; it doesn't find serial parts automatically, but it shows the execution timeline so that you can see them. Besides serial bottlenecks, it can be useful for seeing where you blocked waiting on I/O and so on: https://blog.gopheracademy.com/advent-2017/go-execution-tracer/ https://blog.gopheracademy.com/advent-2017/go-execution-trac... There might already be similar tools for Rust; I know less about what's available there. (Go folks also added some tools for tracing across RPCs, which is less relevant to what you said but just kinda independently neat: https://medium.com/observability/debugging-latency-in-go-1-11-9f97a7910d68 https://medium.com/observability/debugging-latency-in-go-1-1...)
- steveklabnik 8y agoSome of this functionality just doesn’t exist (no way to track gc when you have no gc), some of it uses C/C++ tooling (syscalls can be straced), some of it is just different (suspend points aren’t implicit), and some of it is on the way (tokio-trace is basically the real equivalent here but either doesn’t quite exist yet or is very new, I forget which to be honest).
- fooker 8y ago> Everything you write in Rust is required to be thread-safe Either you have a poor understanding of thread safety or Rust has solved the halting problem.
- legulere 8y agoCould you explain why solving thread safety is equivalent to solving the halting problem? I suppose you also include the absence of deadlocks into thread-safety? Often it means just freedom from data races.
- rbehrends 8y agoFreedom from data races is far too weak a condition to be useful. Consider the following example: you have a single global mutex, and every read/write to memory is bracketed by a lock/unlock of that mutex. The resulting code, while horribly inefficient, is technically free of data races, but that does not really give you anything interesting in terms of thread safety. Any useful definition of thread safety requires that you can reason about program behavior in the presence of concurrency. One such approach is the idea of interference-freedom introduced by Owicki and Gries [1]. Somewhat simplified, assuming that you have a thread A with precondition P and postcondition Q, i.e. the Hoare triple { P } A { Q }, a thread B does not interfere with thread A if the parallel execution of A and B, given the same precondition P we can still prove Q ({ P } A || B { Q }), for any interleaving of A and B. Obviously, proving such a property is undecidable in the general case. In practice, we therefore limit ourselves to simpler models that are easier to reason about by constraining how threads can interact with one another (e.g. transactions, actor model), but even then, you can still end up with situations that are undecidable in the general case. [1] https://dl.acm.org/citation.cfm?id=2697004 https://dl.acm.org/citation.cfm?id=2697004
- vmchale 8y agoRust is nice, but it's missing the ease of parallelism you get from things like Haskell's accelerate or repa. I prefer it for concurrency, but it's not a panacea. In particular, laziness is necessary for efficient immutable data structures, and immutable structures are a nice way to avoid some of the problems inherent in writing concurrent programs.
- samatman 8y agoIs this actually true? Clojure is eager and has efficient immutable data structures.
- vmchale 8y agoSome immutable data structures must be implemented strictly, others lazily. There are known algorithms/data structures where you either have to use immutability or laziness in order to get an efficient implementation.
- nestorD 8y agoNote that lazy / eager is just a default : it just take more lines of code to write lazy datastructures (such as Okasaki's ones).
- steveklabnik 8y agoCheck out the im crate.
- vmchale 8y agoThere's nothing lazy in there, right?
- steveklabnik 8y agoI don’t 100% remember, but it’s efficient immutable data structures, written by Bodil, who I assume you know.
- scottlamb 8y ago> Everything you write in Rust is required to be thread-safe. I wouldn't put it like that. You can (and almost certainly do) have data structures on the stack or heap that are not thread-safe (by which I mean they suffer from data races if accessed from multiple threads). I'd say that Rust enforces that stuff that's available to multiple threads be thread-safe. That covers global variables (which are always assumed to be available to multiple threads; maybe this is what you mean by "doesn't even have a concept of a single-threaded program"). It also covers stuff passed to another thread in other ways (passed to the thread at construction time, sent over a channel, etc). Rust describes thread safety with two traits: "Sync" (a type for which it is safe to share references between threads) and "Send" (a type that can safely be transferred across thread boundaries). The book talks more about these here: https://doc.rust-lang.org/book/ch16-04-extensible-concurrency-sync-and-send.html https://doc.rust-lang.org/book/ch16-04-extensible-concurrenc... The neat thing is these are automatically implemented when doing so is safe[1], and the compiler requires them at the key spots. You don't need to rely on some half-trusted comment or other manual annotation declaring something as thread-safe or audit all the using code if you want to make something no longer thread-safe; code just won't compile if it's expecting something to be thread-safe that isn't. The compiler tells you what the problem is, and in most cases, fixing it is simple: wrap something in a Mutex, replace a Rc with Arc (non-atomic reference count with an atomic one), replace a RefCell with a Mutex or RwLock. If it compiles and doesn't say "unsafe", there's no data race. This is what people mean when they say Rust has "fearless concurrency". I'm not aware of any other language that guarantees lack of data races without giving up mutability or shared state. (There are limits to the fearlessness, of course. You can still have data races by doing something dumb in code marked "unsafe". And Rust doesn't protect you against deadlocks at all, so you need to consider lock ordering and such as you would in most other languages. You can still have contention problems because your locking is too coarse. etc.) [1] when you're doing something unsafe you might need to manually declare something as Send and/or Sync because the compiler can't figure that on its own that doing so is safe. I've encountered this when doing FFI; another case would be when implementing synchronization primitives. Most code shouldn't have to deal with this.