8 ms·
Sadly it seems the "solution" there is "don't do CSP in Rust".
by kinofcain 13y ago
Sadly it seems the "solution" there is "don't do CSP in Rust".
- masklinn 13y agoNot quite. It's "don't do retarded things using CSP in Rust".
- ZoFreX 13y agoOh, come on. Not only is your comment needlessly insulting but it's completely unconstructive. What types of CSP things should you do in Rust, then? Which should you not? Why is this code not a demonstration of a slowness in Rust but a 'bad idea'? How would you implement it in a more idiomatic manner in Rust?
- coldtea 13y ago>Oh, come on. Not only is your comment needlessly insulting but it's completely unconstructive. I find his comment pretty spot on. If anything it was the "don't do CSP in Rust" comment that was unconstructive and passive aggresively insulting. Instead of understanding what he read and the constrains it shows, the commenter preffered to just piss on the language. As for the example, it was obviously "retarted" or rather, contrived. I mean, heck, it's "chinese whispers" with 10000 channels, what more do you need to see that this is not a serious way of solving actual problems with CSP? It's a bloody microbenchmark, and those are rarely representative of anything. >What types of CSP things should you do in Rust, then? The types of CSP things that people use threads and processes for. E.g getting lots of computations going on in parallel, not incrementing tens a single int in each of thousands of "computations". CSP and channels are meant for getting real (cpu intensive) work done. You don't just use threads (or greenthreads) "because concurrency". That some Go programmers like to use channels as a control mechanism, doesn't mean CSP was meant for that kind of abuse. That's like using Exceptions for control flow to me. Isn't it obvious that even in Go the times to complete this are horrible (just an order of magnitude less horrible because of different design trade-offs), and that doing the same job of 10,000 incrementings properly would complete in 1/1000 the time?
- redbad 13y ago> CSP and channels are meant for getting real (cpu > intensive) work done. You don't just use threads (or > greenthreads) "because concurrency". The whole point of green threads is that they're orders of magnitude cheaper to create (and destroy) than real, operating system threads. They are precisely around "because concurrency" -- they allow you to nicely model concurrent problems without having to be overly concerned with the implementation detail of their creation cost. While it's not idiomatic in Go to use a channel/goroutine combo as an iterator, for example -- that's too low-level -- it's absolutely idiomatic to use one for other types of higher-order control flow, managing state machine transitions, doing a scatter/gather, and so on.
- deleted 13y ago[deleted]
- strcat 13y agoRust tasks have the same large fixed-size stack as OS threads. A fine-grained concurrency model like a task graph would be build on top of them.
- redbad 13y ago> Rust tasks have the same large fixed-size stack as OS > threads. A fine-grained concurrency model like a task > graph would be build on top of them. In the absence of other context (I don't really know much about Rust) I would then argue that Rust tasks miss the point of CSP.
- pcwalton 13y agoI think that's unfair. There's no absolute threshold that defines "the point of CSP"; you might equally say Go's goroutines miss the point of CSP because they're slower than the equivalent for loop. Besides, the only substantive difference between Go's implementation and Rust's implementation is that Go uses segmented stacks, while Rust does not. That is because segmented or relocating stacks are in opposition to Rust's design goals of no GC, fast calling into C, and predictable performance.
- duaneb 13y agoCSP is still easily workable with rust so long as you don't define it as the ability to rapidly launch tasks in a tight loop.
- hdevalence 13y agonot the GP, but: println((N+1).to_str()); Rudeness of the person you're replying to aside, I think it's well explained elsewhere in the thread that the Rust people have made choices that don't necessarily give great speed on pointless microbenchmarks (making a bunch of processes that do nothing) in favour of performance on CSP tasks where your CSPs actually do things.
- pcwalton 13y agoThe solution is "don't spawn a huge number of tasks in a tight loop". Rust tasks are optimized for maximum performance once you spin them up, at the cost of some overhead once you spawn them. That was a deliberate choice, because not having segmented stacks makes many much more important things far faster, such as calling into C and performing tight sequential computations without thrashing on stack boundaries or using a garbage collector to move stacks around and rewrite all the pointers. Rust is about making costs explicit: when every function call could turn into a huge malloc(), you lose a lot of predictability over performance (and Go found this out too from what I gather). Calling into C is a big one: we concluded it was basically impossible to make Rust calling into C as cheap as C calling into C in a segmented stack implementation. In practical usage, we found that the ability to call into C cheaply was much more important than the benefits of small stacks, which mostly help microbenchmarks like this at the expense of real-world code.
- samth 13y ago> In practical usage, we found that the ability to call into C cheaply was much more important than the benefits of small stacks, which mostly help microbenchmarks like this at the expense of real-world code. For Rust's goals, I can definitely believe that the first is true. I don't think the second claim is true at all, though. Lightweight threads have been extremely successful in Haskell, Erlang, etc, and not just on microbenchmarks.
- pcwalton 13y ago> I don't think the second claim is true at all, though. Lightweight threads have been extremely successful in Haskell, Erlang, etc, and not just on microbenchmarks. They're mostly successful in languages that GC/heap-allocate all stack frames (paying the costs that come with it). Rust uses the machine stack so the benefits come with some very significant drawbacks, most notably unpredictability of performance that comes from stack thrashing. Large mallocs are slower than small mallocs in general, but on the typical sizes you need for machine stack segments you probably aren't going to hit the malloc implementation's free list anyway, so you might as well just allocate up front. If you GC stack frames, though, you just bump allocate in the nursery, at the cost of greatly increased GC pressure (but it makes task spawning much cheaper). There's some interesting discussion on this near the end of Simon Marlow's paper "Extending the Haskell Foreign Function Interface with Concurrency": http://community.haskell.org/~simonmar/papers/conc-ffi.pdf http://community.haskell.org/~simonmar/papers/conc-ffi.pdf