Live data from Hacker News

Local async executors and why they should be the default

maciej.codes

151–160 of 327 posts

Re: Local async executors and why they should be the default

#151
post #125
post #113

Earlier quoted context omitted.

I really think in the end, in another decade or two, the community consensus is going to be that async as it is conceived of today is simply a mistake, full stop. Think about it. Where did it come from in its current incarnation? Node. Why did Node choose it? Did it have a multiplicity of options and carefully choose the best one based on years of experience with each choice? No. Async was "chosen" because it was the…

> Where did it come from in its current incarnation? Node. Rust's async/await is inspired by C# (which was inspired by F#, which was inspired by Haskell). > right now threaded code is straight-up a better option on almost every metric, Agreed that people are generally too eager to reach for async when they could easily get away with threads, and fortunately Rust makes threads extremely pleasant to work with. The whol…

See other post about why I blame node.

As for Rust supporting threads... yes! Yes it does! And it effectively solves all of the problems with them that can be solved at the programming language level. There's an irreducible increase in complexity with concurrent code, but there's only so much to be done about that.

I think a lot of developers have a tendency to massively overprivilege performance and always reach for the biggest, shiniest thing when they don't need it, and cost themselves a lot in the process. Sure, if you're doing tens of millions of things concurrently in Rust you may need the async support because threads can't do what you want. But before you write code based on the assumption that you're going to be doing tens of millions of concurrent things on a 64-core system, check that you actually are. If you've got an API server whose request rate is measured in tens per second, a really quite common case, you'll never notice the thread overhead. You really need quite a massive system nowadays to get to the point where that's even remotely a problem, let alone your biggest one.

I don't deny that when you are nailed to the wall and seriously wondering if 64 cores is going to be enough you may have specialized needs that require specialized solutions. I am glad that Rust has the option for those who need it. But async should be counted as a cost of such problems, not a benefit, and something you prove out as the only solution, not the first thing reached for.

Re: Local async executors and why they should be the default

#152

Earlier quoted context omitted.

Sounds to me like you are dealing with this at the process boundary, more like actor and message based software. This is a mature approach and will serve you well. The typical context for stuff like async/await is code that should be running in a separate process so it can block but doesn't. Then it gets ugly fast.

Yes, I suspect you're right. I do not like building giant monoliths with internal task systems. I prefer using async/await on the inside and microservices with rpcs or, ideally, streams for communication. I think perhaps people are trying to push too much into a single process?

> I think perhaps people are trying to push too much into a single process?

That's exactly what's happening. A bit of Erlang/Elixir experience would help a lot of people to create architectures (even in other languages) that do not require such kludges. If your application starts to take on the kind of complexity that you normally deal with a monolithic OS kernel you're doing something terribly wrong.

Re: Local async executors and why they should be the default

#153

Earlier quoted context omitted.

Why is runtime needed for a syntactic feature like that? All you need is an async wrapper for system calls, and every sync function could, in principle, be compiled down to a coroutine and ran on an executor of your choice. Just because Go has a runtime and has a go keyword, doesn't in itself mean that runtime is necessary for the go keyword to exist.

Any goroutine can be interrupted to let another goroutine run on the respective OS thread, without the involvement of the code author. The thing managing this scheduling behind the scenes is the runtime. Also, the entire standard library is built atop non-blocking IO, which again is managed by the runtime behind the scenes (e.g. waiting for file descriptor readiness, scheduling the respectively blocked goroutine, etc…

Sure, but I'm not saying "implement all Go features in Rust" - all I'm saying is "use function call prefix to asynchronize function calls instead of synchronizing them". What exactly makes that particular syntactic construction troublesome? Compiler could compile it exactly the same way it's doing now.

Re: Local async executors and why they should be the default

#154
post #113

Earlier quoted context omitted.

I really think in the end, in another decade or two, the community consensus is going to be that async as it is conceived of today is simply a mistake, full stop. Think about it. Where did it come from in its current incarnation? Node. Why did Node choose it? Did it have a multiplicity of options and carefully choose the best one based on years of experience with each choice? No. Async was "chosen" because it was the…

Saying that Rust chose async because node did is such an absurdly ignorant, incorrect statement.

Rust didn't choose async because of Node. Async's massive popularity is because of Node. It existed long before then, and that's precisely part of my point. It had a bad reputation before then, and it's slowly-but-surely reacquiring it now.

Re: Local async executors and why they should be the default

#155
post #150
post #113

Earlier quoted context omitted.

I really think in the end, in another decade or two, the community consensus is going to be that async as it is conceived of today is simply a mistake, full stop. Think about it. Where did it come from in its current incarnation? Node. Why did Node choose it? Did it have a multiplicity of options and carefully choose the best one based on years of experience with each choice? No. Async was "chosen" because it was the…

I used/worked on a similar async model at FB long before I'd ever touched node. Threads are not really solving the same problem as async. Imagine you are building the FB newsfeed. You have 10 stories and want to fetch them as quickly as possible. The stories are all different types and each refers to other data: profile info, privacy, comments (which depend its own profile fetch), likes, etc. So you have this tree of…

I used "async" long before Node too. It's one of the reasons I knew to stay away from Node.

"ad-hoc implementation of async"

I think you're confusing the problem with the solution. I solve this sort of thing in Go all the time. This is literally what I was doing yesterday. It's fine in that context. You had to reach for async because it was the only option in your context, not because it was the best choice. Async as it is conceived of today was a hack for dynamic scripting languages that basically had no other option. They "won" there because the environments simply couldn't support any other solution, not because they outcompeted anything.

Re: Local async executors and why they should be the default

#156

Earlier quoted context omitted.

Any kind of mechanism like that requires a mental model that carries a ton of state because it is no longer immediately visible what the scope of execution is. Continuations and the way Erlang handles this have far less mental overhead and help keeping the model and the mental representation of that model in sync. Differences between the two is where bugs will hide. The web isn't asynchronous it's synchronous in almo…

> Continuations and the way Erlang handles this have far less mental overhead and help keeping the model and the mental representation of that model in sync. Differences between the two is where bugs will hide. You still have a lot to think about in Erlang. For example, you need an entire supervisory system to handle the fact that an actor can die. You need to handle the fact that an actor A might send a message to a…

The supervisory system is an extra, you don't technically need it but it can help make your application bullet proof and I would definitely recommend if you go the Erlang route to use it to your advantage.

Every other language and/or runtime will need something similar anyway, but there is a good chance that it won't be nearly as elegant (as as solid) as the way Erlang does this.

Agreed that actors are not something that you can slap onto an existing system, but you can build systems that use a lot of those lessons effectively without them being written in Erlang.

> It's really not that bad. Like, IMO the major reason to have async is for cancellation. I want to be able to have a network request time out. That is way, way easier to handle in async code than threaded code. The best I can do with a sync system is specify timeouts on an underlying resource like a socket, I can't do anything like "this specific request should have this specific timeout" without async.

That's a great way to end up with orphaned resources though. If you kept it at the process level that simply could never happen, your process would error out, the supervisor would take over and that would be that.

Re: Local async executors and why they should be the default

#158

Just a personal take: after not coding with Rust for several months, I find it more and more difficult to return to an async code I was writing. The whole thing just reads... ugly and inconsistent. It needs too much already-accumulated knowledge. As the article correctly points out, you need a bunch of stuff that are seemingly unrelated (and syntactically speaking you would never guess they belong together). And as o…

Rust sweetspot is really for use cases where any kind of automatic memory management is forbidden, either due to real use case requirements (high integrity computing, kernel drivers,...), or due to existing domain culture that frowns upon any other kind of alternatives.

For everything else, there are more productive alternatives, even Go, which after generics is kind of ok.

Re: Local async executors and why they should be the default

#159
post #47

Earlier quoted context omitted.

It's OK but not great. Ideally library authors should provide a sync facade so people don't have to deal with this, like reqwest does.

Ideal for library users but not for library authors...

The implementation is really not complicated, though it’s hardly great if you expect it to be performant: reqwest just creates a tokio runtime, schedules the task on it, waits for completion, then tears it down.

It’s repetitive but it’s simple.

Re: Local async executors and why they should be the default

#160
post #144

Earlier quoted context omitted.

Thanks for this reference. Will bookmark for if I ever need an HTTP service. Wish this also provided websockets. EDIT: it does https://github.com/tomaka/rouille/blob/master/src/websocket/... Looks a bit neglected though. Not much recent development.

There is also Astra https://github.com/ibraheemdev/astra

This looks nice, thanks.
Post reply on HN