Live data from Hacker News

Rust without the async (hard) part

lunatic.solutions

71–80 of 136 posts

Re: Rust without the async (hard) part

#72
post #35

Earlier quoted context omitted.

If your program is mostly synchronous, you can manually create the async runtime and just use block_on to call async functions from a sync context: https://tokio.rs/tokio/topics/bridging#a-synchronous-interfa...

No, this not work well. The highly infectious nature of async means you need to do that A LOT. ie: reverse ALL things await. That is too much. I refactor all my codebase (a huge refactor!) because this.

What would you prefer the alternative to be? Library authors to do dual implementations of everything?

Re: Rust without the async (hard) part

#74
post #55

Earlier quoted context omitted.

This "async virality" syndrome is the main reason why async is harmful imho. _Some_ async can be very useful in certain constrained circumstances, I believe. However forcing the async execution model on all code is a terrible idea.

Yes. I've been saying this for some time. I call it "async contamination". The async model assumes you spend most of your time waiting for your slow users to do something. (Why a web site, which is inherently stateless, should be doing that routinely is another issue.) I'm writing a metaverse client that has about 10-20 threads, many of them compute bound, running at different priorities. Works fine, but is totally d…

Sorry, offtopic, but what do you mean by "metaverse client"? I've seen you mention this in a couple comments now and I'm intrigued. I don't imagine you mean something to do with Facebook, right?

Re: Rust without the async (hard) part

#75
post #29

Earlier quoted context omitted.

Sometime ago I was comparing go, python and Rust to do some GET request asynchronous. At first, I noticed that the go version was actually faster than the Rust one, and then I saw that in `reqwest`, they recommend you if you're doing multiple GET request, to create a `Client` and then use that to get better performance[1]. After changing my code, the Rust version was effectively a bit faster (not by much, to be hones…

Python’s request is exactly the same (the client is called Session). I guess the go client just uses a global connection pool by default?

Yes - `DefaultClient` in `net/http` is what the various package level methods operate on. This is constitutionally bad as global state that dependencies can mutate at will during init (or any other time), hence go-cleanhttp [1].

[1]: https://github.com/hashicorp/go-cleanhttp

Re: Rust without the async (hard) part

#76
post #72
post #35

Earlier quoted context omitted.

No, this not work well. The highly infectious nature of async means you need to do that A LOT. ie: reverse ALL things await. That is too much. I refactor all my codebase (a huge refactor!) because this.

What would you prefer the alternative to be? Library authors to do dual implementations of everything?

A language with a function-color-agnostic effect system, generic over asynchronicity?

Re: Rust without the async (hard) part

#77

Does anyone else get the feeling that we (as a field) are missing something basic about concurrency? Like there's a really elegant solution just around the corner, that has the low overhead of async/await without the complexity. Or otherwise put, the ease of goroutines but without GC. I know it sounds crazy. I recently dove into the area, and was pretty surprised at how many interesting building blocks there are out…

A decade ago the simple thing we were missing about threaded concurrency was Rust's ownership and borrowing model and Send/Sync. Before that, the simple thing was to use early Java, which had a mandatory garbage collector and monitor objects. If you didn't have or use those, then you were subject to memory safety problems. And moving from heap-scanning GC to ownership and borrowing gave a genuine performance advantage.

Now, we want to remove threading from the concurrency story, in the hopes of getting another performance boost. This itself is the problem, because threads were giving us automatic preemption, akin to how GCs were giving us automatic memory safety. Now we have to statically determine a "good time" for the program to yield. I/O yielding is the easy part, and the reason why people are flocking to async; but we also need to support yielding for fairness reasons. Kernels can do this because they have interrupt timers; but there's no lower-overhead equivalent for userspace code that I'm aware of.

The other problems mentioned with async Rust are particular to Rust itself. The language has a policy that heap allocations only ever happen in `std`, because they want to support embedding Rust into applications where heaps don't exist. This means that futures need to be structs. Rust does support structs of indeterminate size, but barely; and there's no support for structs that can grow. Such a thing is likely unsound without a way for the compiler to check growth limits, and the memory is pinned, so we can't grow beyond a preset limit set at the start of the future[0].

Async infects everything it touches because it's a total pain to write networking library code that's preemption-agnostic. Monad would fix that, but higher-kinded traits aren't a thing in Rust yet and we would need lots of language tooling (akin to `?`) to make this ergonomic to use.

There's also just the possibility that we've been engineering the wrong fix, and we should be trying to get OS threads to be as lightweight as possible rather than trying to move the entire threading system into userspace. There's no particular reason why we need 8MB stacks, other than the fact that compilers don't check stack growth themselves. (Which, BTW, is also a soundness hole in Rust as far as I know.)

[0] Go gets around this with a linked list of stacks, which adds its own overhead.

Re: Rust without the async (hard) part

#78
post #73

Earlier quoted context omitted.

FTFY: thousands of GREEN threads

Does it matter? The point is that Go has excellent throughput and latency, while using only a single concurrency model.

Yes, it does matter. It has excellent throughput and latency for certain classes of systems, while others are impossible to build. Rust may not impose this constraint while meeting its goals.

Re: Rust without the async (hard) part

#79
post #42

Earlier quoted context omitted.

I'll use an embedded analogy. I'm not as familiar with concurrency on GPOS, but consider this: I have an I/O task that might take long, compared to CPU operations: - Start the task, but don't wait for its result. - Your program continues as normal - When the IO task is complete, its hardware sends an interrupt (at a specific priority) to the CPU. The CPU stops what it's doing (assuming there isn't a higher priority t…

I have no idea what GPOS stands for, but the analogy isn't really necessary. The high level algorithm you describe is basically how async programs work. Glossing over the low level details, you usually implement things in terms of polling. Interrupts and their analogs are far too slow at scale (switching async tasks is in the nanoseconds, these days). The problem is when there is logic downstream of the task that nee…

I think GPOS in this context stands for General-Purpose OS (as opposed to embedded).

Re: Rust without the async (hard) part

#80
post #9
post #3

Anything using the green/lightweight or OS thread model is usually easier to use at the cost of some runtime performance. Whether the runtime performance matters for your use case can only be determined by measuring stuff. The perception that async rust is where you should start for concurrent rust because it's built in and everyone uses it perhaps should be revisited. I would argue that the other options are worth c…

Imo, 99% of the time, ergonomics should take precedence over power. Power can always be added later with clever hacks, without ruining an ergonomic interface. But adding ergonomics to power is a much more broken process.

That's a reasonable choice of priorities to have, but it's the opposite of Rust's. Rust prioritizes (1) safety, (2) performance, (3) ergonomics, in that order. There are other languages that make put ergonomics before performance but they are generally unsuitable for Rust's niche.
Post reply on HN