Live data from Hacker News

How to think about async/await in Rust

cliffle.com

181–190 of 268 posts

Re: How to think about async/await in Rust

#181
post #3

Earlier quoted context omitted.

Your comment seems to be conflating concurrency with parallelism. JS doesn't have any language-level abstractions for parallelism (async or not) but you do have Web Workers[0] and process forking (depending on runtime) to get actual parallel programming. JS async deals with concurrency, not parallelism. Threads are the opposite: They are interfaces for parallel programming and their use is orthogonal to how your appl…

> Your comment seems to be conflating concurrency with parallelism. No, it really doesn't. I mention both concurrency and parallelism, and their main difference. > Threads are the opposite: They are interfaces for parallel programming No, they are not. Threads can do both. When waiting for an i/o bound operation, a thread can simply sleep. Added bonus: A thread basd implementation supports io bound concurrency and cp…

> When waiting for an i/o bound operation, a thread can simply sleep.

I mean if you're fine with blocking I/O then obviously you don't need async, but on the other hand having non-blocking I/O is the whole point of async ^^

Re: How to think about async/await in Rust

#182

Earlier quoted context omitted.

Is the idea that you might be fanning out and want to delay starting the event loop? Fanning out in JS looks like: const fn = async (arg) => { ... }; // calls some RPC const results = await Promise.all(args.map(fn)) which might technically be starting the first func before the second is in the event loop, but I don't see why that matters for what I'm doing.

It does matter for what I'm doing.

You said most use cases, so I was thinking of a typical backend that's handling requests, calling RPCs, doing DB queries, etc.

Re: How to think about async/await in Rust

#183

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 is, in many situations, better than traditional threads. Threads are a resource hog. They take a lot of system resources, and so you usually want to have as few of them as possible. This is a problem for applications that could, in theory, support thousands of concurrent connections, if not more. With a basic thread-based model, you need 1 thread per connection, and if you have long-lived connections with infre…

> if you have long-lived connections with infrequent traffic

This is an interesting case. Is it difficult to recover state in the case of an error in such a connection? If not, then you could just use that ability. If so, that seems fragile.

Also, this doesn't sound like an inherent limitation of the design approach. Couldn't the linux kernel just improve the performance of that case?

> those threads mostly do nothing but consume precious system resources.

You mean a small amount of virtual memory?

Re: How to think about async/await in Rust

#184

Earlier quoted context omitted.

Last time I used Rust was long ago, before it even had async, so idk what the outcome looks like. What kinds of libs depend on async in an infectious way? Cause if they didn't, the only alternatives seem to be spawning threads or asking for a threadpool.

HTTP client library is the one that got me yesterday. "reqwest" has a blocking mode, but it's less featureful than its async version, and the code I needed to do signing etc was only possible with the async API. I don't think this is the right way to design libraries ; I think the right thing to do is to expose the state machine etc that drives the I/O and then provide async and sync entry points to the same thing. B…

Yeah, seems like HTTP client libs should just be used in blocking/sync mode, and the user can wrap the calls in async if desired. I said the alternative is a threadpool under the assumption that the lib actually needs concurrency, but this doesn't.

Now that you mention it, this reminds me of ObjC where promises were a cool new thing and got put into tons of libs for no reason. The fad died down after a year or so.

Re: How to think about async/await in Rust

#185

Earlier quoted context omitted.

Isn't that problem generally easily solved with a thread pool ? (that's what nginx does I believe)

There are use cases where a thread pool doesn't solve your problem. If you're handling a few short-lived connections at a time, it's more than enough, but if you're developing something like a push / messaging / queuing service, with thousands of clients connected for hours at a time and receiving very little data once every few minutes, a thread pool won't help you.

> a thread pool won't help you.

What do you see as the main limitations of spawning 2048 threads in a pool in this scenario?

Re: How to think about async/await in Rust

#186

Earlier quoted context omitted.

> One advantage of async/await is that its easier to cancel things. For example, this leads to the design pattern where you have multiple futures and you want to select the one that finishes first and cancel the rest. > In regular threaded programming, cancellation is a bit more painful as you need to have some type of cancellation token used each time the thread waits for something. This a) is more verbose and b) ca…

I think the comment was about async in general, not just Rust (although that's the topic of OP). In Python, cancellation causes an exception to be injected at the await site, which allows it to clean up whatever resources it likes (even if that means making other async calls). If you use Trio or the new TaskGroup in asyncio (inspired by Trio) then an exception leaking out of one task causes the others to be cancelled…

> In Python, cancellation causes an exception to be injected at the await site, which allows it to clean up whatever resources it likes (even if that means making other async calls). If you use Trio or the new TaskGroup in asyncio (inspired by Trio) then an exception leaking out of one task causes the others to be cancelled, and then the task group waits for all tasks to complete (successfully, with exception, or cancelled). It's extremely nice and easy to write reliable programs.

But not in JavaScript.

Re: How to think about async/await in Rust

#187

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…

It began in C# in 2012 - 5 years before JavaScript. And C# does threads. And made its way to JavaScript via Typescript (whose creator also created C#).

Yes, I remember when my head was blown when I finally understood what this new async/await thing is doing in C#. It was nice to observe other language adopt it with basically the same syntax (Javascript, Python, Hack, then Rust with slightly different syntax). I had some satisfaction that my language got it first

C# really helped to popularized asyc/await as language feature, even though F# had something similar first.

Re: How to think about async/await in Rust

#188
post #93

Earlier quoted context omitted.

> One advantage of async/await is that its easier to cancel things. For example, this leads to the design pattern where you have multiple futures and you want to select the one that finishes first and cancel the rest. > In regular threaded programming, cancellation is a bit more painful as you need to have some type of cancellation token used each time the thread waits for something. This a) is more verbose and b) ca…

Since rust follows RAII, any and all resources allocated in the context should be deallocated when their destructor (the drop trait) is called. The unfortunate exception to this are resources which require an async call to deallocate properly. Though this can be worked around and there is work being done to fix this properly.

> Since rust follows RAII, any and all resources allocated in the context should be deallocated when their destructor (the drop trait) is called. The unfortunate exception to this are resources which require an async call to deallocate properly. Though this can be worked around and there is work being done to fix this properly.

Yeah as I said async does not, in fact, "provide easily cancellable execution patterns".

Re: How to think about async/await in Rust

#189

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…

This is an occurence of co-Blub paradox https://reasonablypolymorphic.com/blog/coblub/index.html

[deleted]

Re: How to think about async/await in Rust

#190

Earlier quoted context omitted.

The alternative with threads on an IO-bound server eventually cycles back to async/await but with extra steps. You write synchronous request handlers until you notice IO-waits wasting all your thread time, add more threads to the pool, start hitting overhead from that, then implement greenthreads. NodeJS did it right, and it's hard to call a 14-year-old technology a fad.

> add more threads to the pool, start hitting overhead from that I would like to know more detail about this claim. The scenario you are describing is one were 64-128 OS threads are fully blocked waiting for IO. If that's the case, is it likely that you will have additional unused IO resources that could be being utilized? Also, what overhead do you see as the main limit on spawning a lot of threads? Is that the CPU…

> The scenario you are describing is one were 64-128 OS threads are fully blocked waiting for IO. If that's the case, is it likely that you will have additional unused IO resources that could be being utilized?

One likely scenario is that you've issued 128 RPCs to some other services and are waiting to hear back. Even if each RPC is, say, on a separate TCP connection, your network stack can handle plenty more.

> Also, what overhead do you see as the main limit on spawning a lot of threads? Is that the CPU time of context switching? If so, in this scenario CPU is not the bottleneck, and switching between processes will be nothing, especially with a 32-64 core CPU.

I don't remember what specific feature of OS threads contributes the most to overhead, and maybe someone else can answer this better. But context-switching burdens both the CPU and RAM (due to saved stacks).

> This is a genuine question

I always assume this anyway. Maybe not on Reddit ;)

Post reply on HN