Live data from Hacker News

Async-std: an async port of the Rust standard library

async.rs

191–200 of 238 posts

Re: Async-std: an async port of the Rust standard library

#191

Earlier quoted context omitted.

A good example is say you want to handle 100k TCP sessions concurrently. You probably don't want to launch 100k threads considering the overhead in doing so and constantly switching between them. You also don't want to do things synchronously as you'll constantly be waiting on pauses instead of doing work on the 100k sessions. So you launch 100k instances of it as an async function and they all stay in a single threa…

Does this mean that rust async is using poll/epoll/kqueue under the hood?

Yes, the executor will use whatever io multiplexing the platform provides (and it’s been coded to support).

If the executor is Tokio, it’s built on mio which will use one of kqueue, epoll or iocp depending on the platform: https://docs.rs/mio/0.6.19/mio/struct.Poll.html#implementati...

Re: Async-std: an async port of the Rust standard library

#192

Earlier quoted context omitted.

Threads are bad for high concurrency. Specifically when you need to call out to another service that has some latency. Say you have 1000 threads. To handle a request each one needs to make 50ms of external or DB calls. In one second, each thread can handle 20 calls. So you can handle 20k requests/second with 1000 threads. But Rust is so fast it can serve 500k requests a second. So with regular threads, you need ~25,0…

Java services have managed for a long time to do just fine. Usually you just have dedicated threadpools for those db/ whatever calls. But yes, eventually, for very heavy cases (more than what I would call "high") you will want async/await.

Which can still be done via java.util.concurrent (Callable, Futures, Promises, Flow) until Project Loom arrives.

Re: Async-std: an async port of the Rust standard library

#193

Earlier quoted context omitted.

Why only 1000 threads? Why not 10k or 100k? With 8k stack for each, you can easy have 10k-100k threads in a low-end system

Let's be real here, its not just the memory requirements, because context switching and the associated nuking of cpu caches are not free. You can go very far with it nowadays, but you can go much farther with async code, if you really need to.

Nobody is denying that async code is faster. But it’s not as dramatic as presented in the grand parent post.

And IMHO the added code complexity is not worth the trouble.

Re: Async-std: an async port of the Rust standard library

#194
post #92

Earlier quoted context omitted.

This is missing a crutial explanation that the underlying OS API are asynchronous.

Yeah for sure. In Java/C# I see people do this all the damn time. Use async method for REST endpoints then make a blocking DB call. Or even worse, make a non-async REST call to another service from inside an async handler. As soon as you do that, your code isn't async anymore. And if you're using a framework like Vert.X or node that only runs one thread per core you're in big trouble. The most reasonable answer I've…

It's possible to write crappy code, async or otherwise.

In my c# world, I use async methods for REST endpoints, which in turn use async calls for anything IO-bound (database, message bus, distributed key store, file system etc). I think more often than not, it's done correctly.

Re: Async-std: an async port of the Rust standard library

#195

Earlier quoted context omitted.

Does this mean that rust async is using poll/epoll/kqueue under the hood?

Strictly speaking, it's not tied to any particular method. It depends on your executor. That said, the most popular executor does use epoll/kqueue/iocp. (tokio)

Doesn't the executor need to be aware of all the different mechanisms that can be used to poll, and so there's an implicit coupling between the async function implementation and the executor?

For example, socket.read() might return a future that represents a read on a file descriptor. I don't know the internals of Rust's async support at all, but presumably the future is queued up and exposes some kind of trait that Tokio et al can recognize as being an FD so it can be polled on using the best API such as epoll_wait() or whatever.

But let's say there's some kernel or hardware API or something that has a wait API that isn't based on file descriptors, and I implement my own async function get_next_event() that uses this API. Do I need to extend Tokio, or the Rust async runtime API, to make it understand how to integrate this into its queues? In a non-FD case, wouldn't it have to spawn a parallel thread to handle waiting for that one future, since it can't be included in epoll_wait()?

Re: Async-std: an async port of the Rust standard library

#196

Earlier quoted context omitted.

> but holds for JavaScript. Held, in 2015 but doesn't any longer since js had async/await. This blog post isn't really interesting anyways, and its popularity mainly comes from the zealotry of gophers.

Nope, it still holds. It’s in fact impossible to call an async function from a sync function and return the result. To use await you have to make the function async which means the caller needs to be async-aware and so on, all the way to the top of the stack. There are hacks like “deasync”, but I personally wouldn’t use it. https://github.com/abbr/deasync Rust can block on an individual future so, say, a sync callbac…

But you don't need `await` to call an async function, you can use a regular function call in a symc and then the function returns (synchronously) a Promise.

What cannot be done is to perform a blocking call on a Promise from a sync function. And that is by design because JavaScript has a single threaded runtime.

Re: Async-std: an async port of the Rust standard library

#197
post #5

This remind me of the blog post "What Color is Your Function?"[0], they had to create a different library that is the same as the standard library but with async functions. I thought Rust had other, better ways to create non-blocking code so I don't understand why to use async instead. [0] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...

> I thought Rust had other, better ways to create non-blocking code so I don't understand why to use async instead. 'async' exists because Python has that GIL bullshit and so Python programmers had to invent that fifth wheel of 'async programming'. Programmers in other languages then got jealous because they, too, wanted a complex, unnecessary framework that pollutes the whole runtime and serves to differentiate regu…

Pretty sure the async/await support in C# predates Python.

C# introduced it in 5.0, which came out in August 2012. The Python proposal (PEP 3156) for an async library was posted in 2012, the proposal (PEP 492) for async/await syntax in 2015, and implemented in Python 3.4 and 3.5 respectively, I believe. So C# predates Python by about 3 years.

From what I can gather, Python was influenced by C#. But C# doesn't have a global lock, and that's not why it has async/await.

Edit: Added PEP reference.

Re: Async-std: an async port of the Rust standard library

#198

Earlier quoted context omitted.

Thanks. Helpful. My question is, in this example: result = await server.getStuff() second = await server.getMoreStuff(result+1) print(result) `await getStuff()` MUST terminate before `await getMoreStuff() ` begins. So this chunk alone is analagous to synchronous code, unless we're in the middle of a spawned task, and there are other spawned tasks in the executor that can be picked up.

Yes, the idea is that the thread that is executing this piece of code can "steal" other work when it is awaiting on either of those methods. Frankly, in the case of sequential flow like the above, I would rather write result = server.getStuff() second = server.getMoreStuff(result+1) print(result) and have the runtime automatically perform work-stealing for me. No need for awaits. They just litter the code. This is wh…

In Go you need to manually tell the runtime to spawn a goroutine with the `go` keyword, which also «litter» the code…

Re: Async-std: an async port of the Rust standard library

#199

Earlier quoted context omitted.

Strictly speaking, it's not tied to any particular method. It depends on your executor. That said, the most popular executor does use epoll/kqueue/iocp. (tokio)

Doesn't the executor need to be aware of all the different mechanisms that can be used to poll, and so there's an implicit coupling between the async function implementation and the executor? For example, socket.read() might return a future that represents a read on a file descriptor. I don't know the internals of Rust's async support at all, but presumably the future is queued up and exposes some kind of trait that…

I slightly mis-spoke in a sense, yeah. This stuff has changed a bunch over the last few years :)

So, futures have basically two bits of their API: the first is that they're inert until the poll is called. The second is that they need to register a "waker" with the executor before they return pending. So it's not so much that the executor needs to know details about how to do the polling; but the person implementing socket.read() needs to implement the future correctly. It would construct the waker to do the right thing with epoll. Tokio started before this style of API existed, and so bundles a few concepts in the current stack (though honestly, an integrated solution is nicer in some ways, so I don't think it's a bad thing, just that it makes it slightly easier to conflate the pieces since they're all provided by the same package.)

Async/await, strictly speaking, is 100% agnostic of all of this, because it just produces stuff with the Futures interface; these bits are inside the implementation of leaf futures. And executors don't need to know these details, they just need to call poll at the right time, and in accordance with their wakers.

I can't wait until the async book is done, it's really hard remembering which bits worked which way at which time, to be honest.

Re: Async-std: an async port of the Rust standard library

#200

Earlier quoted context omitted.

Let's be real here, its not just the memory requirements, because context switching and the associated nuking of cpu caches are not free. You can go very far with it nowadays, but you can go much farther with async code, if you really need to.

Nobody is denying that async code is faster. But it’s not as dramatic as presented in the grand parent post. And IMHO the added code complexity is not worth the trouble.

> And IMHO the added code complexity is not worth the trouble.

The thing is, this is just that - your opinion, generalized as The Truth. But engineering is about making the right trade-offs. Often threading will be fine, you'll win simplicity, and all is good. But sometimes you really need the performance, or your field is crowded and its a competitive advantage. Think large-scale infrastructure at AWS, central load-balancers, or high-freq-trading.

Post reply on HN