How does Rust async compare to Goroutine, Erlang threads, Javascript async, Java async in performance and memory usage? Is there any benchmarks for that?
Comparison of Rust async and Linux thread context switch time and memory use
61–70 of 201 posts
Re: Comparison of Rust async and Linux thread context switch time and memory use
#62Earlier quoted context omitted.
Those benchmarks measure one very specific scenario: serving lots of small requests concurrently. Async handles that well because that's exactly the scenario where a single epoll_wait() call will return lots of events.
Is there a different benchmark that demonstrates a scenario where synchronous syscalls are better suited?
Re: Comparison of Rust async and Linux thread context switch time and memory use
#63Earlier quoted context omitted.
Those benchmarks measure one very specific scenario: serving lots of small requests concurrently. Async handles that well because that's exactly the scenario where a single epoll_wait() call will return lots of events.
Is there a different benchmark that demonstrates a scenario where synchronous syscalls are better suited?
But yeah, it's be super interesting to actually see that demonstrated - that'd be quite a lot of work, however.
Re: Comparison of Rust async and Linux thread context switch time and memory use
#64Earlier quoted context omitted.
Those benchmarks measure one very specific scenario: serving lots of small requests concurrently. Async handles that well because that's exactly the scenario where a single epoll_wait() call will return lots of events.
Is there a different benchmark that demonstrates a scenario where synchronous syscalls are better suited?
Whether you read one file after the other sequentially, or try to read all of them concurrently, won't make a difference, because your Disk/RAM bandwidth is going to be bottlenecked anyways.
Trying to do this concurrently requires more work that won't pay off, so it might actually be slower.
Re: Comparison of Rust async and Linux thread context switch time and memory use
#65That's a huge help. I only need about 20 threads in Rust, some of which are compute-bound. So involving "async" is totally the wrong tool for the job. Goodbye, Tokio.
The only reason you wanted to use “async” was because of micro-optimization on thread context switches?
What other reason were you thinking of?
Re: Comparison of Rust async and Linux thread context switch time and memory use
#66Earlier quoted context omitted.
Well, it can, but not always. Remember that if you’re waiting for an event to arrive, that generally involves a syscall, the thread being put to sleep, and then the thread being woken up. Any time you’re doing that, think, “Could I just replace this polling system with a call to read()?” What io_uring does do is provide a way to poll without needing to wait, but if you haven’t received new events when you poll, you’r…
> What io_uring does do is provide a way to poll without needing to wait, but if you haven’t received new events when you poll, you’re not on the fast path any more. Isn't "not on the fast path any more" a bit absolutist? io_uring's "slow" path is roughly one syscall per iteration, right? That's still many fewer syscalls than one syscall per IO operation (or more if any return EAGAIN/EWOULDBLOCK) as you'd be doing wi…
Right, so if a blocking API makes 1 syscall, io_uring would make N syscals for N iterations.
Re: Comparison of Rust async and Linux thread context switch time and memory use
#67A meaningless comparison. Linux, being a preemptively multitasking OS, switches thread contexts regardless of what you're running. So the Rust async context switch is on top of the regular Linux context switch, not instead .
When you're in sub microsecond time scales, preemption events are relatively rare.
"Linux thread context switch time" is a meaningless metric, since Linux will switch thread context regardless of what you choose to run on your computer.
Any "async" switches are additional overhead; you don't get to not have kernel preemption just because your Rust thread is now switching contexts "asyncly".
There are benefits to having an additional user-mode scheduling mechanism inside your kernel thread, but saving CPU cycles isn't one of them.
Re: Comparison of Rust async and Linux thread context switch time and memory use
#68Earlier quoted context omitted.
The only reason you wanted to use “async” was because of micro-optimization on thread context switches?
That's the most commonly given reason for using async/await so a lot of people assume threads are way more heavyweight than they actually are. What other reason were you thinking of?
This is a great example in Node on useful combinators that with async await make it easy to express parallel programming concepts with familiar tools. No manual IPC, no fork/join child PID/thread ID handling, etc.
https://github.com/sindresorhus/promise-fun
The same abstractions (or many of them) exist in Rust, but I think the above is illustrative of the ways we can combine async object returning functions and then use await to hide the complexity of the state machines needed to drive them.
That this abstraction that makes code easy to read and write also performs better is the icing on the cake. The former prevents bugs and keeps code quality high, and that is worth much more.
Re: Comparison of Rust async and Linux thread context switch time and memory use
#69Re: Comparison of Rust async and Linux thread context switch time and memory use
#70I am not surprised that the cost of context switching due to I/O readiness can often be roughly equal between async tasks and kernel threads. Normal blocking I/O can be surprisingly efficient because of various factors, such as a reduced need for system calls. Think about it this way—if you have a user-space thread which wakes up due to I/O readiness, then this means that the relevant kernel thread woke up from epoll…
Google found that the main cost of context switching isn't really in the syscall boundary but in the task scheduling. That's why their linux fork has optional userspace scheduling of kernel threads with the switchto syscalls[0]. Essentially, if your thread already knows which thread should run next, it can context switch to it without having to schedule in the kernel, which is exactly the situation in these bucket br…
(E.g., don't switch to the systemd or sshd thread if a customer's web request is timing out.)
That said, doing this right is out of reach of the average programmer, and it's doubtful that the compiler has enough domain-specific knowledge to do this automatically.
"Async" of the Python and node.js fame is yet another thing, a hack to get around their interpreters' inability to use kernel multitasking features because of global locks.