Live data from Hacker News

Comparison of Rust async and Linux thread context switch time and memory use

github.com

131–140 of 201 posts

Re: Comparison of Rust async and Linux thread context switch time and memory use

#132
post #119

Earlier quoted context omitted.

Speaking of futures_unordered and similar patterns, I think a part of the "async promise" that has failed is the lack of concurrency for a single user request by default in most languages. That is, the 'easy' path is to write code such as the following (in vaguely C# pseudocode): var p = await GetUserPermission( username ); var c = await GetServerConfig(); var m = await GetMessageOfTheDay(); Assume each await call is…

That .NET syntax using Task.WhenAll seems quite bad, which might be part of the reason why not many people bother (disclaimer: I don't do C# or ASP.NET). In Rust it would be: let p, c, m = join!( GetUserPermission(username), GetServerConfig(), GetMessageOftheDay() ); (you don't even have to write await when using the join macro) With such simple syntax available it seems obvious to me that one would want to use it as…

var r = await Task.Whenall(f1,f2,f3); Console.WriteLine($"{r[0]}, {r[1]}, {r[2]}");

f1,f2,f3 are all async fn's, that is all you have to do

Re: Comparison of Rust async and Linux thread context switch time and memory use

#133
post #126

Earlier quoted context omitted.

It seems to me that these are two orthogonal topics. One thing is how you represent tasks, either using OS threads or async tasks. And the other is how you structure concurrency. Maybe I'm missing something, but I think there is nothing preventing the use of those structured concurrency patterns using OS threads as the base for tasks. Then you get some nice benefits of doing this such as proper stack-traces and easie…

The advantage of async tasks for structured concurrency lies in task cancellation, which is intrinsically linked to the notion of "task ownership". If you are using an OS thread to offload some task, and then realize that you don't need that task's result anymore, your safest bet is to let the thread run until the end and then discard the results it produces. Other options include adding custom cancellation logic to…

POSIX thread cancellation has existed with defined (though complex) semantics for ages. It's a ginormous ugly mess, but it is an alternative to run-to-completion or custom logic.

Re: Comparison of Rust async and Linux thread context switch time and memory use

#134
post #75

> People often see that there's some theoretical benefit of async and then they accept far less ergonomic coding styles and the additional bug classes that only happen on async due to accidental blocking etc... despite the fact that when you consider a real-world deployed application, those "benefits" become indistinguishable from noise. However, due to the additional bug classes and worse ergonomics, there is now le…

more expressive and powerful can also mean harder to wrap tour head around.

Re: Comparison of Rust async and Linux thread context switch time and memory use

#135
post #115
post #101

Earlier quoted context omitted.

That is until you get a weird error or try to do something more complicated.

This is perfectly valid for NodeJS, Python (async and/or threads, hello GIL[0]), and a host of other languages/runtimes. Also I agree that multi-threaded Rust is probably the best alternative to async Rust. [0] I have no problems with the GIL, but it's yet another factor to consider when a python multi-threaded program stops working as intended

Not sure how you can compare then. You can’t do with the threads what you can do with async functions.

Re: Comparison of Rust async and Linux thread context switch time and memory use

#136
TLDR: Async code will have much lower CPU utilization compared to threaded code. An async version of a program might run just as fast as a threaded one, but it will overall use less system resources. The threaded version will be easier to write.

You can also have lower RAM overhead per thread if you choose a smaller stack space. Many programs will run fine with a smaller stack space, BTW.

----

Years ago I had to build a load simulator in C#. The CTO looked at me and told me that it had to simulate 100,000 clients; thus it had to be async.

He arranged for me to have a very powerful computer to run the load simulator.

I originally wrote non-blocking code. The non-blocking code had very low load at 100,000 clients, but I hit a problem with a difficult-to-understand edge case.

Because we only had a weekend to do load testing, I refactored the load simulator to be threaded. It only took me 20 minutes or so. The problem with the difficult-to-understand edge case went away, but CPU usage went up dramatically.

We had to tune the .Net framework to use a much smaller stack space.

In the end, I was able to have 100,000 threads to run the load simulator. CPU usage and RAM usage were very high, but the load simulator ran fine.

If I had more time, I would have taken the time to understand the edge case and continue to use non-blocking code. Then the program would have used much less system resources, but ran just as fast.

Re: Comparison of Rust async and Linux thread context switch time and memory use

#137
I think the async benchmark could be faster still when pinned to a single core if a single threaded runtime was used, and possibly if a single-thread channel implementations were used, but then it's becoming a bit academic. Really, what async gives you is a programming style that's very similar to using blocking sockets, but allows one to achieve select() like performance when doing I/O. That, and it allows one to not have to have a special thread for timers or, even worse, a thread per timer, as that's hidden away by the async runtime implementation and just works.

Re: Comparison of Rust async and Linux thread context switch time and memory use

#138

Earlier quoted context omitted.

The only reason you wanted to use “async” was because of micro-optimization on thread context switches?

That's the most commonly given reason for using async/await so a lot of people assume threads are way more heavyweight than they actually are. What other reason were you thinking of?

I find async/await to be easier to use than alternatives. I see two categories:

- A. Async/await - compiler saves and resumes functions.

- B. Message based - Golang, Erlang, threads with messaging.

With category A, I can use my IDE to jump to every function that is called and easily follow the computation.

With category B, all of these connections happen at runtime with messages.

When you have a tree of tasks all which may save/resume many times, async/await it easier to understand than launching a thread per IO event.

Re: Comparison of Rust async and Linux thread context switch time and memory use

#139

Earlier quoted context omitted.

I've been using C# for around 20 years, basically since it was first released. I never personally had any issue with working with threads and locks, finding it simple enough to reason about them, though I understand lots of people felt differently. When async/await first came to C# around 10 years ago, I grumbled because I didn't see the point; I found it much harder to reason about the flow of code, and initially at…

I find your comment about stack traces a bit weird: of course, when all your work is sequential and you can use only threads, you will have a nice stack trace for free, when async stack traces need a lot of support from the tooling. But most of the time you not only use thread, but also several synchronization primitives (locks, channel, etc.) and when doing so, regarding stack trace you are in an even worst situatio…

Maybe if you spray threads around at random :), but in real-world use I find it much easier to pinpoint where the problem occurred, and the path taken to get there. Also, at least with threads you can get the thread ID and/or name.

Regarding shared, mutable state - if multiple async "threads" can access that state, then you still need to guard it, but usually with an async-capable means.

Re: Comparison of Rust async and Linux thread context switch time and memory use

#140

Earlier quoted context omitted.

Then why is it that IO-heavy benchmarks such as the Techempower web benchmark are dominated by async frameworks? The fastest results there are all from async frameworks [1]. And among Rust frameworks the same pattern holds. The fastest Rust frameworks are async while a synchronous frmework such as Rocket is about 20x slower. [1] https://www.techempower.com/benchmarks/#section=data-r20&hw=... [2] https://www.techempow…

Rocket is slow because of its design, not because it is insufficiently asynchronous.

It's beaten by half a dozen sync Ruby implementations, which should be a pretty good hint that something else is going on.

Lack of HTTP keep-alive is probably the most obvious thing holding it back.

Post reply on HN