4 ms·
Rust started on this route but decided easy C interop and no required runtime were more important and trying to have everything made green threads slower than n
by amaranth 4y ago
Rust started on this route but decided easy C interop and no required runtime were more important and trying to have everything made green threads slower than native threads.
- likeabbas 4y agoThey wouldn’t have had to include a runtime. They just needed to set a standard interface for kernel threads and user threads and then the community could’ve built the runtimes in libraries like they’re currently doing. But they didn’t create those interfaces early enough so the community built them and now the async ecosystem is so fragmented you can’t build libraries generically for async runtimes or for both async and sync
- pkolaczk 4y ago> is so fragmented you can’t build libraries generically for async runtimes How does futures.rs work generically with different async runtimes then?
- likeabbas 4y agoIt doesn't work for all async runtimes like Monoio
- amaranth 4y agoA different runtime wouldn't be able to make the compiler use a different stack allocation strategy (like Go's segmented stacks), for that the compiler needs to know what you're doing. Even if they also did that via having two ABIs for every platform (green vs native) it would essentially bake in a particular runtime as you wouldn't be able to experiment with these lower level primitives, only how you interface with them. If they ever define a stable ABI having two ABIs depending on thread type would also fragment the ecosystem at least as bad as the async runtimes do. As far as async runtime fragmentation you can write code that is generic across runtimes, it's just less convenient to use. If your code only works with plain data and futures you're fine on any runtime, it's when you want to spawn new tasks or block on a task that you have to have a runtime dependency. Async vs sync is the classic "what color is your function" problem which green threads kind of solves but not completely so you still have to understand what is happening so you don't stall every task on a native thread with CPU heavy or blocking work.
- saghm 4y ago> now the async ecosystem is so fragmented you can’t build libraries generically for async runtimes or for both async and sync This is true, but I think there are a few notable caveats: 1. Although you can't generically build libraries per runtime, it is possible to right a library supporting an explicit set of runtimes with some boilerplate; the simplest way is to just to abstract all of the async primitives you want into an API to use internally, use feature flags to implement them based on which runtime you want to support, and then only use that API in the rest of your library (which both avoids the need for conditional compilation outside of that one wrapper module for async stuff and reduces the surface of places you'd need to update to add or remove support for a runtime or if you want to update a runtime to a version that isn't backwards compatible. 2. Although there are quite a lot of runtimes that exist in the ecosystem right now (and more could enter the scene in the future), in practice the usage of a lot of these runtimes is quite small. For a few examples, at the time of my looking up, tokio has 66.7 million downloads overall and 10.3 million "recent" ones; async-std has 9 million overall and 1.3 million recent; smol has 1.6 million overall and 158k recent. There's diminishing returns for each new runtime you add support for, so while there's some up front burden in terms of supporting more than one runtime (like I describe above), the burden over time is not going to be super high. 3. Rust has a history of starting out by providing lower-level primitives for things, letting the community iterate over various ideas in the space for a few years, and then eventually settling on a single or small set of pseudo-official crates for them. I think the error handling APIs are the best example of this; Rust 1.0 launched with the standard library Error trait and the `try!` macro, and the community iterated over a bunch of potential solution (error-chain, failure, and probably a bunch of others I don't even remember at the moment), and for a few years it was a bit messy. Meanwhile, the standard library added the `?` operator and added a replacement for one of the `Error` trait methods (deprecating the old ones), and eventually the churn settled down and most people just use `thiserror` and `anyhow` now. There's still some lingering things to standardize like backtraces for errors, but overall the error handling space is way less fragmented now than basically at any point in the past. I'd argue that the async runtime churn is already starting to trend towards equilibrium; if this turns out to be the case, the costs of supporting multiple runtimes will continue to go down, and I'm guessing (hoping?) that within a few years people will have either mostly standardized on a single runtime or we'll have a standard solution for wrapping the small set of runtimes that retain any significant usage in the community.