Live data from Hacker News

Tokio 1.0 – async runtime for Rust

tokio.rs

331–340 of 422 posts

Re: Tokio 1.0 – async runtime for Rust

#331

Earlier quoted context omitted.

Pending async waits have stacks to preserve too

The difference, at least in the way this is built in Rust, is that when you create a task, you get a single allocation that's exactly sized. There's no resizing, which means that you aren't getting stacks that are too big or too small, with all of the other runtime shenanigans that that entails.

That said, the future has the size of the biggest state that need to be kept across await. The future might be slightly oversized, but still order of magnitude smaller than a perfectly sized stack.

Re: Tokio 1.0 – async runtime for Rust

#332
post #146

Earlier quoted context omitted.

I had the same thing initially. The upside of async over a simple event loop is, in my experience, when things become less simple, and you end up with hard-to-read little state machines all over the place. With async, you can have your event loop, but the state machines are handled by the compiler. Code is like threaded code. That can be very convenient. Threads, obviously, accomplish the same thing, and arguably mor…

I've been amused to watch how Rust now does a simple blocking HTTP request. A few years ago, you used the "hyper" crate, which was a convenient wrapper around the "http" crate. Now, you're supposed to use "reqwest", which is a convenient wrapper around the "hyper" crate. "Reqwest" uses the Tokio machinery, even for a blocking request. If you turn on "Trace" level logging, you can watch it start up a thread pool and g…

But then that is nothing inherent to Rust, it's a choice made by a particular external library. That is probably the worst downside of async, it splits the ecosystem. Reqwest probably tries to offer both choices by hiding one behind the other (although what you describe sounds excessive - running a single task with tokio's single-threaded executor is actually quite lightweight).

That split between "sync" and "async" was always there, for networking libraries. E.g. on C/C++ you find libs that run on libevent, or Boost.Asio, or other varieties, and they don't mix, so you end up spawning a seperate thread - exactly the same thing.

And this is, IMHO, how we should see Tokio + async: as a more ergonomic libevent.

Re: Tokio 1.0 – async runtime for Rust

#333

Earlier quoted context omitted.

It's clear this is one of your hobby horses. Every comment thread here is encumbered with you pointing out that you wouldn't like it if async were the default, fair enough. In fact, you don't seem to like the idea in general. ctrl-f "sanxiyn" yields 28 instances, most of them restating in every subtree the same point about how you think threads > async. Since I think most of us tend to read the comments section top t…

You may want to contribute something concrete. I did learn some new advantages of async over threading from replies, besides tired C10K. Yes, async is useful for C10K. No, I am not solving C10K problem. 1. If you use async in single thread mode, you can save thread synchronization. 2. async works better for idle connections and slow connections, even when the absolute number of connections is not large. 3. async task…

I work at a company where I do routinely need to handle C10K problems.

I also routinely interview candidates that want to work at my company. We typically downlevel or turn away candidates who do not have experience solving C10K problems (unless they can appropriately fake that experience).

Even though you may not need to solve C10K problems, (like in any education) it is typically very useful for engineers to think about and attempt solving artificial C10K problems to better educate themselves for when they need to solve those problems.

Meanwhile, if you're the CTO of a company and truly know your business will not require C10K ever in its life, and you know this is the wrong time to educate yourself and your employees, then yes you're correct that async is the wrong abstraction for you right now. Frankly in that case I'd argue Rust may also be the wrong abstraction for right now.

Re: Tokio 1.0 – async runtime for Rust

#334

Earlier quoted context omitted.

Can you elaborate a bit what it is that you find difficult or undesirable about Tokio? Or async/await + some runtime in general? So, I can relate to not wanting to pull in the dependency. But otherwise it seems pretty straightforward to me. You just macro-decorate the main function, sprinkle some async/await around, maybe add a join or a mutex somewhere, and then pretty much forget all about event loops, messaging, t…

> just macro-decorate the main function, sprinkle some async/await around, maybe add a join or a mutex somewhere, and then pretty much forget all about event loops, messaging, threads and whatnot that is ... not how I have experienced it. I work on building highly concurrent systems every day but async drives me insane. to me the fundamental issue is that although the code now reads linearly, it no longer executes li…

Thank you for the perspective! This actually explains it very well. So, in a nutshell, it's the difference between apparent and actual complexity, as well as a trust issue.

With async/await the apparent complexity gets reduced at the cost of vastly increased actual complexity. E.g., now instead of everything being your code that you can look at and reason about, all your concurrent workloads disappear in this void that promises to do the right thing with them. If it works the way you intended, great. If it doesn't, the rabbit hole can now be really deep.

And then, it's also a trust issue. Now you have to trust other people to have done a good job.

Ok, yes, this makes sense.

Re: Tokio 1.0 – async runtime for Rust

#335
post #146

Earlier quoted context omitted.

I had the same thing initially. The upside of async over a simple event loop is, in my experience, when things become less simple, and you end up with hard-to-read little state machines all over the place. With async, you can have your event loop, but the state machines are handled by the compiler. Code is like threaded code. That can be very convenient. Threads, obviously, accomplish the same thing, and arguably mor…

I've been amused to watch how Rust now does a simple blocking HTTP request. A few years ago, you used the "hyper" crate, which was a convenient wrapper around the "http" crate. Now, you're supposed to use "reqwest", which is a convenient wrapper around the "hyper" crate. "Reqwest" uses the Tokio machinery, even for a blocking request. If you turn on "Trace" level logging, you can watch it start up a thread pool and g…

Yes I had the same reaction - it seems unreasonably complicated to do a simple, blocking HTTP request in Rust.

Re: Tokio 1.0 – async runtime for Rust

#336
post #332

Earlier quoted context omitted.

I've been amused to watch how Rust now does a simple blocking HTTP request. A few years ago, you used the "hyper" crate, which was a convenient wrapper around the "http" crate. Now, you're supposed to use "reqwest", which is a convenient wrapper around the "hyper" crate. "Reqwest" uses the Tokio machinery, even for a blocking request. If you turn on "Trace" level logging, you can watch it start up a thread pool and g…

But then that is nothing inherent to Rust, it's a choice made by a particular external library. That is probably the worst downside of async, it splits the ecosystem. Reqwest probably tries to offer both choices by hiding one behind the other (although what you describe sounds excessive - running a single task with tokio's single-threaded executor is actually quite lightweight). That split between "sync" and "async"…

> it's a choice made by a particular external library

Yeah I think it points to a culture problem. In some ways because dependency management is so easy with Cargo, I think it creates the temptation to just throw in some dependencies to make something work without truly understanding the overall complexity of what you're creating. Something very similar happens in the NodeJS world.

> it splits the ecosystem

This is something I've really noticed with Rust: it almost seems like there are really two things: Rust, and Rust+Tokio. I'm a bit ambivalent about Tokio being baked into so many libraries: I think it's great to have as an option, but once I decide to use one library built around Tokio, it imposes a lot of constraints about how the flow of control is going to work in my program.

Re: Tokio 1.0 – async runtime for Rust

#337
post #77

I don't really get these modern async APIs. In languages like Javascript I thought they only made sense because JS interpreters are (historically) single-threaded, so you really have no choice but async to express some concepts. Fine. But in Rust you can just spawn threads, share data through channels or mutexes, use OS-provided async IO primitives to poll file descriptors and do event-driven programming etc... I tri…

I've not done nearly enough multithreaded programming, so this may be out of my depth, and what I'm saying and asking maybe completely wrong.

Isn't async by design more efficient even when you have multiple threads you can spawn? My understanding is the event loop would do async tasks in the thread's quiet times, and put those tasks to sleep while it's waiting for io and other things, meaning the thread isn't blocked. Comparing that to (my understanding of) threads, while you're not blocking the main thread, you're still blocking the spawned threads while waiting for io.

Isn't this thread blocking something you would want to avoid if you can regardless of whether or not you have additional threads to play with?

Re: Tokio 1.0 – async runtime for Rust

#338
post #335

Earlier quoted context omitted.

I've been amused to watch how Rust now does a simple blocking HTTP request. A few years ago, you used the "hyper" crate, which was a convenient wrapper around the "http" crate. Now, you're supposed to use "reqwest", which is a convenient wrapper around the "hyper" crate. "Reqwest" uses the Tokio machinery, even for a blocking request. If you turn on "Trace" level logging, you can watch it start up a thread pool and g…

Yes I had the same reaction - it seems unreasonably complicated to do a simple, blocking HTTP request in Rust.

Give ureq a try: https://github.com/algesten/ureq It is blocking only and has a pretty simple API.

Re: Tokio 1.0 – async runtime for Rust

#339
post #77

I don't really get these modern async APIs. In languages like Javascript I thought they only made sense because JS interpreters are (historically) single-threaded, so you really have no choice but async to express some concepts. Fine. But in Rust you can just spawn threads, share data through channels or mutexes, use OS-provided async IO primitives to poll file descriptors and do event-driven programming etc... I tri…

I think async was inevitable for Rust, for better or for worse. My experience is that just using OS-provided threads isn't good enough for say a high performance webserver—compare the pre-tokio hyper benchmark results to the post-tokio ones for example. And Go-like green threads aren't really possible in Rust given the choice to have no runtime. (Having a stack for every coroutine also probably isn't as good for sque…

I was trying to look for what channel I used back in the days I needed to write so called half-async code, but I can't find it anymore. If I remember correctly, at least the 0.1 version of futures had a channel with the other end being sync and the other async.

Today I'd maybe take a look into crossbeam and their queue implementations for this kind of synchronization.

https://docs.rs/crossbeam/0.8.0/crossbeam/

Re: Tokio 1.0 – async runtime for Rust

#340
post #146
post #77

I don't really get these modern async APIs. In languages like Javascript I thought they only made sense because JS interpreters are (historically) single-threaded, so you really have no choice but async to express some concepts. Fine. But in Rust you can just spawn threads, share data through channels or mutexes, use OS-provided async IO primitives to poll file descriptors and do event-driven programming etc... I tri…

I had the same thing initially. The upside of async over a simple event loop is, in my experience, when things become less simple, and you end up with hard-to-read little state machines all over the place. With async, you can have your event loop, but the state machines are handled by the compiler. Code is like threaded code. That can be very convenient. Threads, obviously, accomplish the same thing, and arguably mor…

> the state machines are handled by the compiler

I think this is the aspect of modern approaches async which I am most ambivalent about. One of the things I have learned about programming over the past 10 years is that I, as the programmer, really want to own the flow of control of my program. Once I hand that over to some other system, usually in the name of convenience, I will start to have issues which are difficult to understand and solve.

For instance, a while ago, I was working on a project which was using making heavy use of RXJava. One of my colleagues pushed a commit, and suddenly CI was failing on a unit test which passed when run locally. It turns out the problem was because the CI server was running tests with a different scheduler, so GC was happening at a different time, creating an NPE which didn't happen locally. Imo when you start to see unit tests behaving inconsistently based on a factor which is completely outside the actual code you yourself have written, this is a sign you are going down the wrong path.

I also wonder how much a lot of the buzz around async actually has to do with the fact that it's a bit brain-bending to wrap your head around at first, as compared to its actual utility. I think for a lot of us as programmers, we enjoy that feeling of understanding something difficult - like when you really get recursion for the first time - and we're attracted to the idea of really fundamentally new concepts being introduced to programming.

But it seems to me that async is one of those concepts which brings us farther away from actually programming the hardware, and puts a kind of middle-man between us and the CPU, and I'm not sure that has ever been a good thing.

Post reply on HN