Hacker News new | ask | show | jobs
by likeabbas 51 days ago
you’re talking to the core of the issue. In no other language did they try to satisfy the case of running on an embedded system vs general purpose computing. Async rust tried to, and came up with a solution that is not great for the majority of programmers writing rust.

I wish to God that the rust library devs would admit to this fact - say that async rust should stay for embedded runtimes usecases, but we shouldn’t be forcing async across the majority of general purpose computing libraries. It’s just not a pleasant experience to write nor read. And it really doesn’t give any performance benefits.

1 comments

I write reams of async rust for a living, and completely disagree with this characterization. The concurrency primitives in the futures crate are able to elegantly model the large majority of places where concurrency is needed, and they are nicely compostable.

More than once, we have wanted to improve the performance of some path and been able to lift the sequential model into a stream, evaluated concurrently with some max buffer size. From there, converting to true parallel execution is just a matter of wrapping the looped futures in Tasks.

Obviously just sprinkling an async on it isn’t going to make anything faster (it just converts your function into a state-machine generator that then needs to be driven to completion). But being able to easily and progressively move code from sequential to concurrent to parallel execution makes for significant performance gains.

I completely disagree. Having to make sure every little function is Send + Sync + lifetime even if it doesn't need it is fucking hell. writing concurrent code with plain kernel threads is so much easier to write and read.

If you just want to build a normal backend service, you can't escape async libraries. Wrapping the async functions with `block_on` is not ideal I'd rather just have access to standard sync primitives that don't need me to bring an entire async runtime into the system.

My ultimate point is - I would be happy if async stayed in its own world. But the fact is async has completely polluted the rust library landscape and you can't escape it. I'm working on a project that I hope to show rust users that async isn't needed for performant backend services, and that the code can be written much simpler without it.

You don’t have to make everything Send/Sync if you don’t need to. Use tokio’s local runtime and spawn_local(), or use one of the other async runtimes.

You also don’t need to spawn() futures to await them. Spawn enables parallelism on the multithreaded runtime, holding join handles, etc. If all you need is to execute concurrent code, though, the various combinators and functions in the futures crate lets you do so without having hard requirements on Send/Sync. The large majority of the concurrent code I write uses nothing specific from the tokio crate, including spawn.

As is often the case in rust, the compiler is also telling you the correct thing. If you’re using the multithreaded runtime and spawning, your code may execute in another thread, so it has to be Send/Sync, and since the ownership of the future is transferred to the executor, it must also be ‘static.

You literally can't use one of the other async run times because of the current state of async/await does not allow library authors to easily write for multiple runtimes - they were written for one runtime in mind and that is just tokio. And if you're pulling in library methods you're still stuck with the method headers they specify.

All of your arguments are just mental workarounds trying to justify how fucked the rust ecosystem is for traditional backend services.

The project I'm working on is specific to making traditional kernel threads faster (150-200 nanosecond context switches compared to 1500-2000 nano seconds for normal kernel threads). It requires a user scheduler but you can swap those out without any changes to how you write rust. In my testing, it's not only faster than async rust but also much easier to write. I hope it convinces people like you, that are hell-bent on defending the current state of async rust, that there are better paradigms and we don't have to be locked in to shitty, verbose concurrent code.

You’re moving the goalposts, and seem to have a vested interest in this that I, frankly, don’t.

Send/Sync/static is not needed on tokio’s local runtime, which doesn’t require any adjustments to your libraries.

Passing data between threads requires Send/Sync/static, except for certain cases like scoped threads, so making OS threads faster doesn’t seem to solve that issue like using a local runtime would.

Many async libraries (though certainly not all) are runtime-independent. If your library doesn’t have to spawn, it is easy to write runtime-independent code. I would like to see some spawn traits brought into std to make it easier to write libraries that have to spawn, though.

I’ll always try new ways of doing things, but you are making the assumption that the way you feel is the way everyone feels, and totally dismissing the opinions of those who don’t. It puts me off of whatever solution you might be proposing, since you clearly don’t have the empathy to understand the full range of positions of the people whose problems you’re ostensibly trying to solve.

I’m not trying to convince you the way you feel is wrong, but you are wrong that everyone thinks writing async code is miserable. There are times where it’s hard, or where the compiler emits confusing messages about async closures being not generic enough, but on the whole I enjoy writing async rust, so shoot me.

I haven’t moved any goal posts - I’ve coded async rust and is miserable compared to normal rust with threads. That has been my point which is why I started down this project.

My entire goal is to show that coding the same server with pre-async hyper vs post async hyper is nicer and more performant than async rust. I hope to show you it in just a few days.