Live data from Hacker News

Zero-cost futures in Rust

aturon.github.io

171–180 of 348 posts

Re: Zero-cost futures in Rust

#171

Earlier quoted context omitted.

I think Rust's approach here makes a lot more sense for the language than Go/CML style M:N would. You can recover most of the M:N ergonomics over time via async/await style syntax. But if your foundation is built on a (comparatively) slow "pthreads in userland" threading model, then you hit a performance ceiling that you can never break through. For a language like Rust, it makes sense to begin in the optimum place a…

I think there's little or no evidence that "you can recover most of the M:N ergonomics over time via async/await style syntax" , despite a decade or so of attempts. I think there's an underlying semantic concern that seems unsugarable. Munificent's http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y... expresses the problem eloquently. None of that means you're wrong about async making "a lot more sense for…

This is currently hotly debated in the C++ committee. Some people want shallow C#, python style generators, while other want proper stackful coroutines a-la Lua (full disclosure: I'm on this group). A third group is trying to mediate and trying to come up with an hybrid stackful model that can be optimized as well the async/await model at least in some cases (I.e. full cps transform and fallback to a cactus stack when invoking non transformable functions).

Re: Zero-cost futures in Rust

#172
post #122

Earlier quoted context omitted.

> I don't want M:N threading as Go implements it. As someone who writes code for enterpricey businesses doing a lot of I/O bound stuff, golang style M:N threading is a godsend over Java's standard library, and other common platforms in that space. Being able to express your code in a sequential manor and still gain the performance offered by implicit fiber,goroutine,w/e scheduling is pretty awesome. With futures, the…

> Being able to express your code in a sequential manor and still gain the performance offered by implicit fiber,goroutine,w/e scheduling is pretty awesome. You don't gain as much performance. On Linux, you don't actually gain that much if anything over 1:1 threading. Most of the benefits of goroutines actually comes from the small stacks, which don't have anything to do with M:N and 1:1 to begin with—they're a featu…

> Why would you write networking code in C in 2016, when there are better alternatives available (like this one)?

Support for kernel bypass networking libraries like ibverbs, DPDK (has an old unmaintained Rust wrapper) and other IO kernel bypass libraries such as SPDK and IOAT.

If Rust supported these libraries, I'd much prefer the future based Rust code to a massive event loop in C.

Re: Zero-cost futures in Rust

#173

Earlier quoted context omitted.

> You use green threads instead of native threads because native threads have space overhead, not because they have time overhead. The main overhead of a thread, green or native, is the stack. The size of the stack is independent of whether you use native or green threads. Go's small stacks are actually made possible by its GC, not its choice of 1:1 or M:N. In musl, for example, you can have 2KB stacks [1] with 1:1.…

I haven't seen a benchmark of huge numbers of native threads vs. a userland scheduler, but I have a hard time imagining that a userland scheduler will beat the kernel's scheduler. The kernel scheduler has a much more global picture of the system compared to userland. Doesn't using kernel threads imply lots of context switches? Doesn't that tend to be expensive in terms of time on modern architectures?

> Doesn't using kernel threads imply lots of context switches?

No, it's actually fewer context switches. That's because each I/O completion event goes straight from the kernel to the code that was waiting on it (1 context switch), not from the kernel to the userland dispatcher to the code that was waiting on it (2 context switches).

Re: Zero-cost futures in Rust

#174
post #142

Earlier quoted context omitted.

> On Linux, you don't actually gain that much if anything over 1:1 threading. You use green threads instead of native threads because native threads have space overhead, not because they have time overhead. Attempting to spawn 100k OS threads will do strange things to most kernel scheduling algorithms; they're not optimized for that use-case.

> You use green threads instead of native threads because native threads have space overhead, not because they have time overhead. The main overhead of a thread, green or native, is the stack. The size of the stack is independent of whether you use native or green threads. Go's small stacks are actually made possible by its GC, not its choice of 1:1 or M:N. In musl, for example, you can have 2KB stacks [1] with 1:1.…

Well, one advantage the Go runtime has is the green threads are cooperatively scheduled, so switches are a lot more lightweight.

Re: Zero-cost futures in Rust

#175

Earlier quoted context omitted.

I think Rust's approach here makes a lot more sense for the language than Go/CML style M:N would. You can recover most of the M:N ergonomics over time via async/await style syntax. But if your foundation is built on a (comparatively) slow "pthreads in userland" threading model, then you hit a performance ceiling that you can never break through. For a language like Rust, it makes sense to begin in the optimum place a…

> You can recover most of the M:N ergonomics over time via async/await style syntax. While i agree with the tradeoff made by rust (although i think the approach used by C++ coroutine is better), i don't think that having async/await syntax give you "most" of the ergonomics of the Go M:N model . The main advantage of the go model is that both asynchronous and synchronous operations are identical, with async/await you…

> although i think the approach used by C++ coroutine is better

How?

> The main advantage of the go model is that both asynchronous and synchronous operations are identical, with async/await you still need to model the async operation and the sync operation with different types.

It's more like "everything is synchronous" in the Go model. Semantically, Go doesn't have async I/O at all. It has a userspace M:N implementation of synchronous I/O. You can get the same effect in any other language by just not using async I/O.

> Not having to decide(and design) upfront which part of your computation is async and which is not is what makes go so attractives

That doesn't make sense to me. If async I/O is important in your app, why not just make your entire app use async I/O?

Re: Zero-cost futures in Rust

#176

Earlier quoted context omitted.

Contrary to popular belief, not all C++ programs (or rust FWIW) are web servers serving HTTP requests over the Internet.

Yep, that's why I'm asking about the use-cases in the grandparent comment.

As an example, many real-time systems are often a giant ball of messy asynchronous code and state machines. Futures can help with that, although lately I have found that somtimes the best, cleanest, way to implement a state machine is to make it explicit.

Re: Zero-cost futures in Rust

#177

Earlier quoted context omitted.

> You use green threads instead of native threads because native threads have space overhead, not because they have time overhead. The main overhead of a thread, green or native, is the stack. The size of the stack is independent of whether you use native or green threads. Go's small stacks are actually made possible by its GC, not its choice of 1:1 or M:N. In musl, for example, you can have 2KB stacks [1] with 1:1.…

Well, one advantage the Go runtime has is the green threads are cooperatively scheduled, so switches are a lot more lightweight.

> Well, one advantage the Go runtime has is the green threads are cooperatively scheduled, so switches are a lot more lightweight.

Switches occur on timeouts (which involve a round trip through the kernel), on I/O events (which also involve a round trip through the kernel), or on goroutine message passing. So in every case except goroutine message sends, the switches don't actually save you a trip through the kernel.

Re: Zero-cost futures in Rust

#178

Earlier quoted context omitted.

Well, one advantage the Go runtime has is the green threads are cooperatively scheduled, so switches are a lot more lightweight.

> Well, one advantage the Go runtime has is the green threads are cooperatively scheduled, so switches are a lot more lightweight. Switches occur on timeouts (which involve a round trip through the kernel), on I/O events (which also involve a round trip through the kernel), or on goroutine message passing. So in every case except goroutine message sends, the switches don't actually save you a trip through the kernel.

Except when the IO doesn't go though the kernel in the first place of course.

Re: Zero-cost futures in Rust

#179

Earlier quoted context omitted.

> You use green threads instead of native threads because native threads have space overhead, not because they have time overhead. The main overhead of a thread, green or native, is the stack. The size of the stack is independent of whether you use native or green threads. Go's small stacks are actually made possible by its GC, not its choice of 1:1 or M:N. In musl, for example, you can have 2KB stacks [1] with 1:1.…

The space overhead is significantly worse for native threading in the presence of many threads since each thread needs both a user and kernel stack. For M:N, there are N kernel stacks.

Fair enough, but kernel stacks are 8K. 10K user + kernel size is a far cry from the 2MB default pthread stack size people usually talk about when they talk about 1:1.

I don't know of any benchmark comparing Go vs. a 1:1 implementation with 2K pthread stack sizes, but I would be surprised if the performance difference is large at all.

Re: Zero-cost futures in Rust

#180
post #155

Earlier quoted context omitted.

I did edit my first question after seeing the numbers.

Arguing about whether a 30% difference is "significant" isn't really interesting.

You're talking about using a language where you have to manually manage the memory, is significantly harder to use than Go, have less built-in concurrent patterns, inferior standard library, so yes 30% seems a very small benefit.

I didn't had look into the benchmark but my guess is that it's waiting on the I/O so any language with good concurrent model will have the same numbers.

The benchmark would have been more interesting if it was more CPU intensive, that would have definitely favored Rust.

it's not about Rust Vs Go, it's just that 30% seems very low when you know how fast Rust is vs Go in some scenarios.

Post reply on HN