Maybe Rust isn’t a good tool for massively concurrent, userspace software
481–490 of 624 posts
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#482Earlier quoted context omitted.
>Trying to solve the problem by frequently invoking signal handlers will also show in your latency distribution! So just like any other kind of scheduling? "Frequently" is also very subjective, and there are tradeoffs between throughput, latency, and especially tail latency. You can improve throughput and minimum latency by never preempting tasks, but it's bad for average, median, and tail latency when longer tasks s…
> So just like any other kind of scheduling? Yes. Industries that care about latency take some pains to avoid this as well, of course. > io_uring and epoll have nothing to do with it and can't avoid the problem: the problem is with code that can make progress and doesn't need to poll for anything. They totally can though? If I write the exact same code that is called out as problematic in the post, my non-preemptive…
Yeah, there's your misunderstanding, you've got it backwards. The problem being described occurs when I/O isn't happening because it isn't needed, there isn't a problem when I/O does need to happen.
Think of buffered reading of a file, maybe a small one that fully fits into the buffer, and reading it one byte at a time. Reading the first byte will block and go through epoll/io_uring/kqueue to fill the buffer and other tasks can run, but subsequent calls won't and they can return immediately without ever needing to touch the poller. Or maybe it's waiting on a channel in a loop, but the producer of that channel pushed more content onto it before the consumer was done so no blocking is needed.
You can solve this by never writing tasks that can take "a lot" of time, or "continue", whatever that means, but that's pretty inefficient in its own right. If my theoretical file reading task is explicitly yielding to the runtime on every byte by calling yield(), it is going to be very slow. You're not going to go through io_uring for every single byte of a file individually when running "while next_byte = async_read_next_byte(file) {}" code in any language if you have heap memory available to buffer it.
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#483Earlier quoted context omitted.
> I don’t see why it would be an either/or. For instance, Rc does not leak memory, and neither do many cases of interior mutability. `Rc` and internal mutability together do allow creating cycles and thus leaking with only safe code. I suggest you to read https://cglab.ca/~abeinges/blah/everyone-poops/ if you haven't done already, it explains the historical reasons for why `std::mem::forget` was changed to be safe. >…
What about the other way? There could be a trait that means “references have the same escape semantics as a stack frame”, perhaps called NotLeaky, and then a variant of tokio spawn could require NotLeaky, and return a non-‘static future. NotLeaky could be inferred in the same way as Send and Sync. As a bonus, high-availability systems could require NotLeaky at the top of their event loop, precluding runtime memory le…
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#484Earlier quoted context omitted.
> Yes, sometimes you can get stuck for a day, trying to express something within the ownership rules. This is a big problem. Fast iteration time is very valuable. And who likes doing this to themselves anyway? Isn't it a very frustrating experience? How is this the most loved language?
> How is this the most loved language? Personal preference and pain tolerance. Just like learning Emacs[1] - there's lots of things that programmers can prioritize, ignore, enjoy, or barely tolerate. Some people are alright with the fact that they're prototyping their code 10x more slowly than in another language because they enjoy performance optimization and seeing their code run fast, and there's nothing wrong wit…
The thing is, for many people, including me, Rust is actually a more productive language than Python or other dynamic languages. Actually writing Python was an endless source of pain for me - this was the only language where my code did not initially work as expected more times than it did. Where in Rust it works fine from the first go in 99% of cases after it compiles, which is a huge productivity boost. And quite surprisingly, even writing the code in Rust was faster for me, due to more reliable autocomplete / inline docs features of my IDE.
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#485I find myself in this weird corner when it comes to async rust. The guy's got a point in that doing a bunch of Arc, RwLock, and general sharing of state is going to get messy. Especially once you are sprinkling 'static all over the place, it infects everything, much like colored functions. I did this whole thing once back when I was starting off where I would Arc stuff, and try to be smart about borrow lifetimes. Tot…
> But then rust also has channels. When you read about it, it talks about "messages", which to me means little objects. Like a few bytes little. This is the solution, pretty much everything I write now is just a few tasks that service some channels. They look at what's arrived and if there's something to output, they will put a message on the appropriate channel for another task to deal with. No sharing objects or an…
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#486Earlier quoted context omitted.
Exactly. People are too afraid of using threads these days for some perceived cargo-cult scalability reasons. My rule of thumb is just to use threads if the total number of threads per process won't exceed 1000. (This is assuming you are already switching to communicating using channels or similar abstraction.)
The performance overhead of threads is largely unrelated to how many you have. The thing being minimized with async code is the rate at which you switch between them, because those context switches are expensive. On modern systems there are many common cases where the CPU time required to do the work between a pair of potentially blocking calls is much less than the CPU time required to yield when a blocking call occ…
"Async" in native code is cargo cult, unless you're trying to run on bare metal without OS support.
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#487Earlier quoted context omitted.
It really is, but I still favour "unsexy" manual poll/select code with a lot of if/elseing if it means not having to deal with async. I fully acknowledge that I'm an "old school" system dev who's coming from the C world and not the JS world, so I probably have a certain bias because of that, but I genuinely can't understand how anybody could look at the mess that's Rust's async and think that it was a good design for…
> It really is, but I still favour "unsexy" manual poll/select code with a lot of if/elseing if it means not having to deal with async. > I fully acknowledge that I'm an "old school" system dev who's coming from the C world and not the JS world, so I probably have a certain bias because of that, but I genuinely can't understand how anybody could look at the mess that's Rust's async and think that it was a good design…
Actually, Rust could still learn a lot from these languages. In Haskell, one declares the call site as async, rather than the function. OCaml 5 effect handlers would be an especially good fit for Rust and solve the "colouration" problem.
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#488Earlier quoted context omitted.
> The difference is that if you get it wrong in a managed language, you get leaks or stale objects or other logic bugs. Can you provide concrete examples of this? I've literally never had a bug due to the nature of a memory-managed language.
You can certainly get memory abandonment, which is like a leak but for memory that's still referenced and is just never going to be used again.
In an ideal world, we could have a GC that reclaimed all unused memory, but that turns out to be impossible because of the halting problem. So, we settle for GCs that reclaim only unreachable memory, which is a strict subset of unused memory. Unused reachable memory is a leak.
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#489Earlier quoted context omitted.
> And who likes doing this to themselves anyway? Isn't it a very frustrating experience? How is this the most loved language? The thing is, these dependencies do exist no matter what language you use if they stem from an underlying concept. In that case rust just makes you explicitly write them which is a good thing since in C++ all these dependencies would be more or less implicit and everytime somebody edits the co…
> So what I'm saying, you need to put in this work no matter which language you choose This is very false. Managed-memory languages don't require you to even think about lifetimes, let alone write them down. Yes, I understand that this is for efficiency - but claiming that you have to think about lifetimes everywhere is just wrong, and irrelevant when discussing topics (prototyping/design work/scripting) where you do…
Memory is only one of many types of resources applications use. Memory-managed languages do nothing to help you with those resources, and effectively managing those resources is way harder in those languages than in Rust or C++.
Re: Maybe Rust isn’t a good tool for massively concurrent, userspace software
#490Earlier quoted context omitted.
Lifetimes are still important in managed languages. You just have to track them in your head, which is fallible. The difference is that if you get it wrong in a managed language, you get leaks or stale objects or other logic bugs. In rust you get compile time errors.
While this is correct, it's still much easier to think about lifetimes in managed languages. The huge majority of allocated objects gets garbage-collected after a very short time, when they leave the context (similar to RAII). Mostly you need to think about large and/or important objects, and avoid cycles, and avoid unneeded references to such objects that would live for too long. Such cases are few. The silver linin…
Those objects are also virtually no problem in languages like Rust or C++. Those are local objects whose lifetimes are trivial and they are managed automatically with no additional effort from the developer.