Live data from Hacker News

Local async executors and why they should be the default

maciej.codes

281–290 of 327 posts

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

#281
> why multi-threaded task executors should be the default

many many reasons and it's subtile and complicated

For one ironically it's just way easier to use, especially for less experienced programmers. In a task system like that it's just way easier to accidentally cause major issues when it's many tasks on one thread compared to multiple threads (at least with the guard rails rust provides for threading safety). On the other hand the performance overhead of by-default multi-threaded is just fine for a ton of use-cases to a point you could argue worrying about it is premature optimization.

Through it's important to state that a lot of this comes from the ecosystem around rust and not rust itself, as stuff like `LocalSet` shows you can have a non-multi threaded runtime and there is no reasons all the libraries you might use couldn't provide non multi thread safe versions. Some do. Just many decided that avoiding the performance overhead of being thread save isn't worth the maintenance overhead and additional foot guns it can provide.

Now naturally you can say "but node/deno/etc." but they are a completely different beast then "just" being not multi threaded by default. Like e.g. they don't have multi threaded code at all. Just single threaded code communicating through serialized messages (kinda). They also e.g. handle all I/O completely separated from you application code and don't have any non serialized communication between threads etc. etc.

Interestingly if you look at the design choices of the I/O Event loop (reactor) of tokio there are some conceptual similarities. Also there AFIK there is a company which is building something similar to the node model for rust using WASM.

I mean in the end the node-style approach is grate for building servers, but rust isn't just for building servers but much more general with much more low level use cases.

Now the main point where rust could improve quite a bit is to make it much easier to write a library which work very efficient with both cases. Code which crosses thread boundaries and code which doesn't. Currently you are often split between either implementing it twice, doing terrible unusable generics tricks or using tons of `cfg` (probably generated using macros/annotations) and hope no dependency accidentally imports the multi-thread feature when you don't need it. Non of this is really viable but it's a surprisingly hard problem. Currently the best idea I can come up for it is generic modules which make the "terrible to use generics tricks" usable, but it's probably not enough by a long stretch. (Even if a solution is found it might not work for Waker, even if it does you still might want sync/send Wakers in some cases.)

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

#282
Rust asyncs design was probably the mostly correct decision for what rust is: A general system programming language which you can use in most situations people today use C,C++ and more.

If rust would only be targeting web server programming the right decision might have been no async and green threads, it's just much easier to use.

But that rust would likely never have succeeded as most of it's initial success cases where for use-cases where you wouldn't want green threads.

Nicely we might still get no async and green threads: In form of run times which run WASM compiled rust code in a node like fashion. Probably in combination with some serverless/edge-compute providers which hopefully will be nice to use.

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

#283

Earlier quoted context omitted.

Keep an eye on gleam lang if you’re not already. It’s a language with an ML inspired type system (like rust) that compiles to erlang. It is likely too nascent to be used in production (in terms of tooling, ecosystem, stability, etc). https://gleam.run

I do already keep an eye on it and I like its syntax a lot. Problem is that the current commercial Elixir ecosystem is very strongly gravitating towards web and API development where several libraries reign supreme (Phoenix [web framework] and many of its dependents and derivatives, plus Absinthe [GraphQL] and Ecto [databases]). They also heavily rely on Elixir's macros so Gleam has quite a lot of work to do before i…

I think part of this is the ego component: it's much more fun to make a relatively big contribution to something new than it is to fix a small part of something much larger.

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

#284

Earlier quoted context omitted.

I do already keep an eye on it and I like its syntax a lot. Problem is that the current commercial Elixir ecosystem is very strongly gravitating towards web and API development where several libraries reign supreme (Phoenix [web framework] and many of its dependents and derivatives, plus Absinthe [GraphQL] and Ecto [databases]). They also heavily rely on Elixir's macros so Gleam has quite a lot of work to do before i…

I think part of this is the ego component: it's much more fun to make a relatively big contribution to something new than it is to fix a small part of something much larger.

That, plus many programmers are former nerds who just love tinkering with stuff and they invent languages as thought experiments. Which is 100% cool with me but it gets weird when they start to promote them...

And figuring out the gap between "wanting to tinker" and "being a reliable paid professional" is a big struggle that takes a while to figure out -- and maybe takes your entire life in maintaining a good balance between both.

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

#285
post #258

Earlier quoted context omitted.

Rust didn't choose async because it was popular. You can go ahead and read the massive, multi-year discussions on the topic if you'd like. They've been going on since before 1.0.

I read them at the time. The gist was basically "it has been empirically established that async is the right way to do concurrency" - ie, it was because of Node.

It was more "it fits best with our needs as a systems language (overloaded term, yada yada) and we should support a robust concurrency model sooner rather than later". The Rust devs were aware of green threads and the like. It is deeply unfortunate that async is still such a mess, though.

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

#286

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…

No, it is completely fair to say that reading and comprehending Golang concurrent code is far-far easier than reading Rust Async code. With Virtual Threads and Structured Concurrency, even concurrent Java code is easy to read nowadays. Rust Async is far more difficult to understand than Rust Sync. You can't just dive in. You need to have coffee and truly focus your mind like you are solving a Math Olympiad problem. I…

The constraints of performance and whatnot limited the options for Rust's async model. It's not bad given the constraints, but it's not great, and how developers use async Rust vs. Go matters even knowing this.

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

#287
post #93

Earlier quoted context omitted.

It's not always that simple. What if your sync lib has some callbacks, and you want to do something in the callbacks that requires async. You could argue that this mismatch exists even with rust itself, where you have Drop, which is sync, and you might have to do something in your drop that requires async, like closing a network connection. You have to pass in some runtime handle that you can use to spawn a task to d…

Within your sync callback spawned via spawn_blocking, you can run async code with Handle::current().block_on(…): https://docs.rs/tokio/latest/tokio/runtime/struct.Handle.htm... .

Which panics if you don't call it in the right context. So now you have code which can only be used under certain circumstances and with one particular async runtime.

Since when has it become acceptable in rust to have fns that just panic depending on the state of some thread local? That is one thing I really dislike about tokio.

The thread local runtime is convenience over correctness, which is antithetical to what rust usually favours.

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

#288
post #167

Earlier quoted context omitted.

And yet it didn't learn that it took almost 10 years for .NET community to sort out async/await support across all layers of the stack. .NET archictects have spent last year researching Go and Java Loom approaches, and have acknowledge if it had been today, most likely that would have been the approach taken instead of async/await, as many .NET devs still get it wrong. During the "ASP.NET Core and Blazor futures, Q&A…

Joe Duffy on Midori and async/await in a C# dialect: http://joeduffyblog.com/2015/11/19/asynchronous-everything/

Where he notes that the .NET implementation wasn't without issues on Midori.

Which is again another point of how the whole Rust's async/await process failed to learn from previous experiences.

Even worse, because in what concerns .NET, the runtime is part of the story, while in Rust that is yet another piece of the puzzle that is in motion, not yet decided, although it can be anything as long it is tokio.

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

#289
post #158

Earlier quoted context omitted.

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.

Automatic memory management is not exclusively positive, even if you don't care about performance. If you lose deterministic destructors and therefore the possibility of RAII, for me it is a loss. I worked with lots of garbage collected languages (Java, C#, Scala) before rust, and I don't miss garbage collection at all...

[deleted]
Post reply on HN