Live data from Hacker News

Async Rust never left the MVP state

tweedegolf.nl

161–170 of 273 posts

Re: Async Rust never left the MVP state

#161
post #64

Earlier quoted context omitted.

There is work happening on keyword generics[0], which would let a function be generic over keywords like `async` and `const`. For now the best option to write code that wants to live in both worlds is sans-io. Thomas Eizinger at Fireguard has written a good article about this[1] pattern. Not only does it nicely solve the sync/async issue, but it also makes testing easier and opens the door to techniques like DST[2] I…

I may have missed something, but how does “sans-io” deal with CPU heavy code? For example, if there’s some heavy decoding/encoding required on the data? Does the event loop only drive the network side and the heavy part is done after the loop is finished?

You won't get any benefits using async with CPU heavy code. Quite the opposite really.

Re: Async Rust never left the MVP state

#162

Agree with the other commenters that the title is a bit too dramatic. The content was well written and got the point across. I still don’t have enough experience to have a strong opinion on Rust async, but some things did standout. On the good side, it’s nice being able to have explicit runtimes. Instead of polluting the whole project to be async, you can do the opposite. Be sync first and use the runtime on IO “edge…

What's the alternative? I'm happy to use tokio, but i'm happy other folks can enjoy other executors (smol, async-std, glommio, etc). I think the situation is OK because tokio is well-maintained, even though it's not part of the standard library, and i'm afraid making it part of the standard library would make it harder to use other executors, and harder to port the standard library to other platforms. But maybe my fe…

Here are some alternatives for concurrent operations in rust that don't use Async. Which are available depend on the target, e.g. embedded/low-level vs GPOS. I use all of these across my Rust projects:

  - Threads and thread pools
  - CPU SIMD
  - GPU
  - DMA, with memory and/or dedicated hardcore
  - Multiple cores, ICs, or MCUs
  - Hardware interrupts
  - Event loops
Most of you are already aware. I bring this up because I have observed that in the Rust OSS community (especially embedded) people sometimes refer to not using Async as blocking, and are not aware that Async isn't the only wya to manage concurrency. People new to it are learning it this way: "If you're not using Tokio or Embassy (Or some other executor), you are blocking a process."

Re: Async Rust never left the MVP state

#163

Async seems like an underbaked idea across the board. Regular code was already async. When you need to wait for an async operation, the thread sleeps until ready and the kernel abstracts it away. But We didn’t like structuring code into logical threads, so we added callback systems for events. Then realized callbacks are very hard to reason about and that sequential control is better. So threads was the right program…

> the thread sleeps until ready and the kernel abstracts it away. Sure, but once you involve the kernel and OS scheduler things get 3 to 4 orders of magnitude slower than what they should be. The last time I was working on our coroutine/scheduling code creating and joining a thread that exited instantly was ~200us, and creating one of our green threads, scheduling it and waiting for it was ~400ns. You don't need to w…

You involve the kernel also when you are doing async io.

In this context the interesting thing to measure would be doing IO in your green threads vs OS threads.

A stronger theoretical performance argument for async io is that you can do batching, ala io_uring, and do fewer protection domain crossings per IO that way.

Re: Async Rust never left the MVP state

#164

Earlier quoted context omitted.

Threads are neither better or worse than async+callbacks. They are different. There are problems which map nicely to threads and there are problems which are much nicer to express with async.

Such as? The entire premise of async is that callbacks were a mistake because they broke sequential reasoning and control. Every explanation of the feature starts with managing callback hell.

The entire premise of callbacks is that threads were a mistake because they broke sequential reasoning and control.

JK, obviously callbacks became prominent as a result of folks looking for creative solutions to the C10K[0] problem, but threads have a long history of haters[1][2][3].

[0] https://en.wikipedia.org/wiki/C10k_problem

[1] https://brendaneich.com/2007/02/threads-suck/

[2] https://web.stanford.edu/~ouster/cgi-bin/papers/threads.pdf

[3] https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-...

Re: Async Rust never left the MVP state

#165

Earlier quoted context omitted.

Nobody seriously tries to run Golang or Java on an MCU. But they do run Rust code.

J2ME existed before most of the current crop of Rust programmers were born.

Which doesn't work on low-end MCUs even in the CLDC profile (< 16 bit, < 32k ram, < 160k rom) and is dead since 2007, proving my point that nobody wants to run it.

Re: Async Rust never left the MVP state

#166

Earlier quoted context omitted.

That's not the core issue at all it's lifetimes and allocations.

Can you elaborate on this please? Do you mean that’s basically impossible for rust std to provide a default runtime that makes “everyone” (embedded on one end and web on the other) happy?

I think that's the problem in essence, yes. Different executors built on top of different primitives and having different executions strategies will have mutually incompatible constraints.

To spawn a future on tokio, it has to implement `Send`, because tokio is a work-stealing executor. That isn't the case for monoio or other non-work-stealing async executors, where tasks are pinned to the thread they are spawned on and so do not require `Send` or `Sync`, so you can use Rc/RefCell.

Moreover, the way that async executors schedule execution can be _different_. I have a small executor I made that is based on the runtime model of the JS event loop. It's single-threaded async, with explicit creation of new worker threads. That isn't a model that can "slot in" to a suite of traits that adequately represents the abstraction provided by tokio, because the abstraction of my executor and the way it schedules tasks are fundamentally different.

Any reasonably-usable abstraction for the concept of an async runtime would impose too many constraints on the implementation in the name of ensuring runtime-generic code can execute on any standard runtime. A Future, for better or worse, is a sufficiently minimal abstraction of async executability without assuming anything about how the polling/waking behavior is implemented.

Re: Async Rust never left the MVP state

#167

Async seems like an underbaked idea across the board. Regular code was already async. When you need to wait for an async operation, the thread sleeps until ready and the kernel abstracts it away. But We didn’t like structuring code into logical threads, so we added callback systems for events. Then realized callbacks are very hard to reason about and that sequential control is better. So threads was the right program…

> Regular code was already async. When you need to wait for an async operation, the thread sleeps until ready and the kernel abstracts it away Not really. I’ve observed async code often is written in such a way that it doesn’t maximize how much concurrency can be expressed (eg instead of writing “here’s N I/O operations to do them all concurrently” it’s “for operation X, await process(x)”). However, in a threaded wor…

> work stealing executors have long been known to offer significantly lower latency with more consistent P99 than traditional threads. This has been known since forever - in the early 00s

Well, we know how to make "traditional threads" fast, with lower latency and more consistent P99 since forever^2, in the early 90s. [1]

Sure, we can't convince that Finnish guy this is worthwhile to include in THE kernel, despite similar ideas had been running in Google datacenters for idk how many years, 15 years+? But nothing stops us from doing it in the userspace, just as you said, a work stealing executor. And no, no coloring.

Stack is all you need. Just make your "coroutines" stackful. Done. All those attempts trying to be "zero-cost" and change programming model dramatically to avoid a stack, introduced much more overhead than a stack and a piece of decent context switch code.

> You can tell async is directionally kind of correct in that io_uring is the kernel’s approach

lol, it is very hard to model anything proactor like io_uring with async Rust due to its defects.

[1] https://dl.acm.org/doi/10.1145/121132.121151

Re: Async Rust never left the MVP state

#169

Agree with the other commenters that the title is a bit too dramatic. The content was well written and got the point across. I still don’t have enough experience to have a strong opinion on Rust async, but some things did standout. On the good side, it’s nice being able to have explicit runtimes. Instead of polluting the whole project to be async, you can do the opposite. Be sync first and use the runtime on IO “edge…

It's very much possible to use rust for a lot of areas with async without needing to be dependent on tokio. I think it's really just the web/server stuff that's entirely tokio dependent. Writing libraries to be executor agnostic is not terribly difficult but does require some diligence which isn't necessarily present in most of the community.

It really depends on the abstraction model of the library. If the library needs to actually read/write a file, it either needs to depend on a runtime or provide some horrific abstraction over the process it will use to do that. This doesn't apply to sync IO libraries which can just use the Standard Library.

Web/server frameworks have to bind to a runtime because they have to make decisions about how to connect to a socket. Hyper is sufficiently abstract that it doesn't require any runtime, but using hyper directly provides no framework-like benefits and requires that you make those decisions and provide a compatible socket-like implementation for sending requests.

Re: Async Rust never left the MVP state

#170

Agree with the other commenters that the title is a bit too dramatic. The content was well written and got the point across. I still don’t have enough experience to have a strong opinion on Rust async, but some things did standout. On the good side, it’s nice being able to have explicit runtimes. Instead of polluting the whole project to be async, you can do the opposite. Be sync first and use the runtime on IO “edge…

So depending on your context, it may seem like the whole ecosystem depends on tokio, but if you look at say, embedded Rust, it makes a little more sense.

The system requirements for an async runtime on a workstation processor compared to say, an RP2040 look very different. But given the ability to swap out the backend, when I write async IO code for a small ARM M0 microcontroller, that code looks almost identical to what I'd be writing outside that context, but with an embedded focused runtime, ie embassy.

I can focus less on the runtime specifics as they use the same traits and interfaces. Compare this with say, using a small RTOS or rolling your own async environment, it's quite nice.

Much of what I need to learn to write the async code in embassy can cross over to other domains.

Post reply on HN