Live data from Hacker News

Local async executors and why they should be the default

maciej.codes

301–310 of 327 posts

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

#301
post #55

Earlier quoted context omitted.

> So e.g. you have to use Arc > In terms of C++ code that would equate to std::shared_ptr > which ... sounds quite wasteful in terms of scalability/performance. Why is it not possible to simply return a Rust promise? That's the way I do it in my C++ async (executor) library backed by work-stealing queues under the hood.

Not sure what you mean by "returning a rust promise". But yes, Arc > is a thread safe smart pointer containing a mutex, which has quite some overhead. You will see this in many places in the internals of async code that has to be Send. OK in many cases, but it can kill you if you have to go through this for many small operations.

Yes, that's what I meant - it's a heavy operation consisting of multiple dynamic memory allocations and atomics. Former can be somewhat addressed through custom allocators and which in my case proved to be a big win for small and short-living tasks.

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

#302

Earlier quoted context omitted.

> So e.g. you have to use Arc > In terms of C++ code that would equate to std::shared_ptr > which ... sounds quite wasteful in terms of scalability/performance. Why is it not possible to simply return a Rust promise? That's the way I do it in my C++ async (executor) library backed by work-stealing queues under the hood.

> Why is it not possible to simply return a Rust promise? It… is? An Arc is what you need to share mutable data between “promises”.

I understand that part. What I don't understand is why https://docs.rs/promises/latest/promises/struct.Promise.html are not used instead.

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

#303
post #32

Earlier quoted context omitted.

the use of async and await is a design decision that requires knowledge of the program's logic and desired behavior. It's not just a matter of compiler optimization, and that's why the compiler can't automatically figure out where to use these keywords. Suppose we have a service where users place orders, and we need to: 1. Save the order. 2. Deduct items from inventory. 3. Send a confirmation email. If we perform the…

You have it backward. The compiler should implicitly add the awaits for waitable objects, unless an operation is explicitly async. So you would write (in pseudocode): Task PlaceOrder(Order order) { SaveOrder(order); DeductItems(order); SendConfirmationEmail(order); } And the compiler will implicitly await all three operations (and ideally infer that your function is async). If you want to overlap computation, you avo…

I appreciate the original comment by ikekkdcjkfke, and your elaboration, gpderetta. However, I perceive a distinction between compiler optimization and proposing a fundamentally different model for handling asynchronicity. It seems to me that what you're suggesting deviates significantly from the existing C# model. Transitioning to this model wouldn't be a simple matter of compiler optimization—it would be a major breaking change that would require rethinking many aspects of the language.

Please don't misunderstand me; I'm not dismissing your proposal outright. However, it's essential to consider the potential trade-offs such as control flow management and error handling strategies. These are currently well-addressed by the async mechanism in C#.

Regarding polymorphic async-ness, I acknowledge this as a minor limitation within the current C# model. However, a common practice involves returning Task or Task, even when no async calls are involved. Moreover, I find Kotlin's approach to handling this through inlining of suspend functions quite intriguing. You can check it out here: https://kotlinlang.org/docs/kotlin-tips.html#the-suspend-and...

In closing, it would be beneficial to our discussion if we could clarify whether we're contemplating an optimization within the current C# framework or proposing a fundamentally different approach to asynchronicity.

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

#304

It's a frustrating area. As I've mentioned before, I'm writing a high-performance metaverse client in Rust, something which has many of the problems of both a web browser and a MMO. If you want to have a good looking metaverse, it takes a gamer PC multiple CPUs and a good GPU to deal with the content flood. (This is why Meta's Horizon looks so bad.) Now you have to use all that hardware effectively. So what I'm writi…

AMD's new processors easily double FPS in modern games

I bought a 3600 in 2019, but a 7800x3D would blow it out of the water after just 4 years due to more cache, hitting 5Ghz, faster DDR5 RAM

https://youtu.be/B31PwSpClk8?t=800

shadow of the tomb raider, 152 FPS to 382 FPS

some games wouldn't double the FPS, but I'm really just looking to hit 280 FPS on my 280Hz monitor

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

#305
post #303

Earlier quoted context omitted.

You have it backward. The compiler should implicitly add the awaits for waitable objects, unless an operation is explicitly async. So you would write (in pseudocode): Task PlaceOrder(Order order) { SaveOrder(order); DeductItems(order); SendConfirmationEmail(order); } And the compiler will implicitly await all three operations (and ideally infer that your function is async). If you want to overlap computation, you avo…

I appreciate the original comment by ikekkdcjkfke, and your elaboration, gpderetta. However, I perceive a distinction between compiler optimization and proposing a fundamentally different model for handling asynchronicity. It seems to me that what you're suggesting deviates significantly from the existing C# model. Transitioning to this model wouldn't be a simple matter of compiler optimization—it would be a major br…

I'm not suggesting that C# makes a change, it is probably too late (although you could easily implement both models), I was just describing my preferred semantics (well, I prefer stackfull coroutines, but that's another story). I don't think it deviates much from the existing semantics, the minimum change is adding awaits automatically for any call to awaitable functions and requiring async annotations otherwise. This is a relatively minor change.

I don't think that the async/sync division is a minor thing, but thanks for the Kotlin reference, I'll take a look. Before watching the video I guess that they implement 'stackfull-like' behaviour in otherwise stackless coroutines by force-inlining any HOF so that the coroutine is again flattened. If that's the case, that's great! What happens if inlining can't happen? They reject the code or convert to stackfull coroutines? That's for me as always been the holy grail: stackfull semantics that optimize to stackless (i.e. bounded stack usage) when possible (i.e. when the compler can see all possible yield points).

I have been trying to figure out (in C++) the subset of the language such as the optimization is always guaranteed (at the very least you need first class and explicit continuations so that the compiler can track them and inlining must be possible). Possibly Kotlin has cracked it.

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

#306
post #288

Earlier quoted context omitted.

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.

> Where he notes that the .NET implementation wasn't without issues on Midori. Indeed. In my opinion and experience async/await is a very leaky abstraction. I think it is definitely not worth it in most languages. Whether it is appropriate for rust I'm unconvinced. I think it is DOA in C++ with its required allocation, but I have yet to use it in anger.

I only have used them in C++/WinRT, as they are a kind of required to handle anything WinRT, and it was kind of ok when doing UWP, although it was much more complex as in WinRT case, it also takes into account how COM/WinRT compartments work.

See https://devblogs.microsoft.com/oldnewthing/20210504-01/?p=10...

Don't miss it.

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

#307

Earlier quoted context omitted.

Sure it can, but all design patterns have their use cases. Generalizing and saying that one is just "terrible" (which your previous comment implied), and people "should learn" is just untrue and an ignorant take. Sometimes it's the only sane solution compared to others, considering project constraints.

Yes, that one is terrible compared to the alternatives. If there is one thing that really irritates me about the way we go about IT then it is that stuff that works and is reliable and well understood gets tossed and replaced by 'new shiny thing' which then eventually undergoes the same treatment. We are eternally stuck in reinventing wheels, rather than that we make real progress in for instance reliability and are…

On first note, yes that is what happens when a language whose original scope was to be a systems programming language, and now wants to do everything.

Regarding Bjarne Stroustrup, he wasn't dragged anything, his C++ is described in "The C++ Annotated Referece Manual" and "The Design and Evolution of C++", everything that happened after that is responsability of WG21, where he only has one vote among 300+ persons.

There are several papers from him where he complains about the direction WG21 is going, like "Remember the Vasa".

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

#308
post #85
post #73

Earlier quoted context omitted.

They're not green threads. Futures are stackless coroutines.

And green threads aren't a panacea. They introduce runtimes, which was something Rust avoided on purpose.

And then everyone has to bundle tokio anyway.

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

#309
post #99
post #97

Earlier quoted context omitted.

Go can do this because it’s closer to Java or C# than C. It has a runtime that gets compiled into every binary. Rust deliberately avoided that by design.

Indeed. That means that the interop story for golang is horrible. Golang can somewha work with libraries with C bindings. But you can not publish a golang lib as a lib.so with C bindings because of the runtime.

Not necessarly, .NET and D have a much better interop story, despite having runtimes.

You can certainly publish lib.so with Go, here is an example,

https://www.ardanlabs.com/blog/2020/07/extending-python-with...

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

#310

Earlier quoted context omitted.

> Why is it not possible to simply return a Rust promise? It… is? An Arc is what you need to share mutable data between “promises”.

I understand that part. What I don't understand is why https://docs.rs/promises/latest/promises/struct.Promise.html are not used instead.

Because there is no “instead”? A promise (or really a task if you’re in an async context, what you link to is really a thread + a pseudo-oneshot channel) allows independently scheduling a unit of work, it’s neither shared nor mutable state.

The promise object you link to is not Clone, so there is no way for two different threads to refer to the same one without adding an intermediate `Arc`, and because all the methods on it operate by value that’s not exactly helpful either as you can’t move the promise out of the Arc.

Same with a tokio::task::Task. A JoinHandle can’t be duplicated (though according to its doc on some platforms it might be duplicatable — if the thread returned a duplicatable value anyway).

Post reply on HN