Live data from Hacker News

How to think about async/await in Rust

cliffle.com

161–170 of 268 posts

Re: How to think about async/await in Rust

#161

Not specific to rust, but I think asynchronous programming in general is a hype. It didn't start because it is so awesome, it started because JS can't do parallel any other way. That's the long and short of it. People wanted to use JS in the backend for some reason. The backend requires concurrency. JS cannot do concurrency. Enter the event loop. Then enter some syntactic sugar for the event loop. And since JS is pop…

I think that's spot on.

More mature model of that is message passing like in Erlang or in a bit more bastardized version, in Golang. And it works there because you can write "normal" code with no colored functions and other baggage that JS-like async brings with it. And it works beautifully on multi-core machines.

async is just strict, shitty subset of that where you're limited either by bad implementation (any single-process scripting language) or language limitations (no GC in Rust would make Erlang/Go-like message passing much harder).

Re: How to think about async/await in Rust

#162
Concurrency is like a single cook preparing many different recipes at the same time, and parallelism is multiple cooks in the same kitchen. Single-threaded processes can use async/await to context switch and it feels like parallelism but it's not unless you're executing each task to its own OS thread.

... and that didn't click for me until I understood that concurrency (async/await) and parallelism (multiple OS threads and processes) are different executions modes which the Tokio runtime in rust allows you combine those different modes.

Re: How to think about async/await in Rust

#164

Not specific to rust, but I think asynchronous programming in general is a hype. It didn't start because it is so awesome, it started because JS can't do parallel any other way. That's the long and short of it. People wanted to use JS in the backend for some reason. The backend requires concurrency. JS cannot do concurrency. Enter the event loop. Then enter some syntactic sugar for the event loop. And since JS is pop…

async/await is about superimposing useful CPU computations with slow I/O operations. For example, you would read data fragment D0 from a network socket, and right before doing any work on it, you ask the OS to fetch data fragment D1, etc. This would is faster than reading and working in distinct time intervals. And despite whatever you seem to believe, it would also be faster than reading and working using thread par…

I should add that the concurrent algorithm I described for processing data from a socket, i.e asynchronously read data fragment (i + 1) then do work on data fragment i, would only be optimal if the throughout of the work you do on the data is higher than reading throughout.

Even if the above condition doesn't hold, you would have to be very careful to do better with threads.

Is it just an arbitrary design decision that NodeJS is single-threaded?

Re: How to think about async/await in Rust

#165
post #145

Earlier quoted context omitted.

But the difference is that wait_all() is blocking the thread, right? Or does it keep running the event loop while it's waiting, so callbacks for other events can be processed on the same thread? If it does the latter, the stack will keep growing with each nested wait call: main -> runEventLoop -> someCallback -> wait_all -> runEventLoop -> anotherCallback -> wait_all -> ... The async/await transformation to a state m…

Yeah it blocks the thread, any other "user work" needs to happen on a different thread. But if you just need multiple non-blocking IO operations run in parallel it's as simple as it gets. (the operating system's thread scheduler is basically the equivalent to the JS "event loop").

Desktop operating systems all have application event loops that run within a single thread because the OS thread scheduler is not the same thing. If you just want an event loop, trying to use threads instead for everything will often end up in tears due to concurrent data access issues.

Re: How to think about async/await in Rust

#166

Earlier quoted context omitted.

async/await allows to do concurrency without the need for explicit synchronization to shared data structures. E.g. I can do: loop { select! { _ = src_channel.readable() => src_channel.read(&mut buffer), _ = dst_channel.writable() => dst_channel.write(&mut buffer), } } without any mutex guarding the buffer, even though the reads and writes happen concurrently and share the same mutable buffer. This is possible because…

> Which obviously would be much harder to get right. Not obvious to me I'm afraid. Using CSP, this is almost trivially easy. All access to the data goes through a guardian thread. Accessing the resource is just sending a message. And mutexes are not hard either.

+1. This approach of having guardian threads/actors etc communicating by messages is just a natural for Rust, too, because it nicely deals with a whole pile of borrow checker & lifecycle issues, too.

crossbeam_channel FTW

Re: How to think about async/await in Rust

#167
The article only barely touches the performance reason for async. Without an explanation of that vs OS threads, it doesn't make much sense what you're trying to accomplish with async. And while you can think of it as the caller having control like the article says, you can also think of it the more typical way, that you're issuing work and getting async responses.

So I would still start at https://rust-lang.github.io/async-book/01_getting_started/02... And if you want to go deeper, think about how you'd build a massive IO-bound server without async (the answer will look like async but with extra steps).

Re: How to think about async/await in Rust

#168

I'm a strong believer in structured concurrency over async. That said, I do not know if there would be an easier way to implement the state machine in the article using structured concurrency over async. Maybe that is actually one place where async would be better. I need to look into that. However, for those in the comments arguing that async is better for I/O-bound stuff, I heavily disagree. I implemented a multipl…

In the broad use case where you have an IO-bound server, how is this solution better than async? It sounds like your non-async solution was to reimplement async, which shows the value of the feature.

I reimplemented async, except that the event loop is only invoked explicitly and under the programmer's control.

I know it doesn't sound like a lot, but it is.

Re: How to think about async/await in Rust

#169
post #149

I'm a strong believer in structured concurrency over async. That said, I do not know if there would be an easier way to implement the state machine in the article using structured concurrency over async. Maybe that is actually one place where async would be better. I need to look into that. However, for those in the comments arguing that async is better for I/O-bound stuff, I heavily disagree. I implemented a multipl…

> So it's equivalent to async, but it's still synchronous. Nice. Based on your description this is equivalent to async/await implemented with callbacks but not async/await implemented via polling.

Do you mean polling, as in calling poll()? Or polling, as in continuously checking if something is finished?

Re: How to think about async/await in Rust

#170
post #145

Earlier quoted context omitted.

But the difference is that wait_all() is blocking the thread, right? Or does it keep running the event loop while it's waiting, so callbacks for other events can be processed on the same thread? If it does the latter, the stack will keep growing with each nested wait call: main -> runEventLoop -> someCallback -> wait_all -> runEventLoop -> anotherCallback -> wait_all -> ... The async/await transformation to a state m…

Yeah it blocks the thread, any other "user work" needs to happen on a different thread. But if you just need multiple non-blocking IO operations run in parallel it's as simple as it gets. (the operating system's thread scheduler is basically the equivalent to the JS "event loop").

Right, so it's less efficient than async.

Async would let you yield at the gather.

Post reply on HN