News for hackers. Stuff that matters.
Re:Zero-cost futures in Rust by soulbadguy on Thursday August 11, 2016 @11:54AM (#12269267)
Finally a nice async/io interface for rust, always felt that it was a big missing piece, couple of questions for peps familiar with async in other languages :

1 - Isn't the state machine approach the same as C#/.net async/await is using ? But the with the added convenience of the syntactic sugar ?

2 - no allocation : , does'nt the lambda closure need to be allocated somewhere ?

3 - I would have love some comparison (booth performance wise and on theory) with C++ up comming coroutine work, from my understand the C++ approach is even more efficient in term of context switching and have the advantage of even less allocation.

Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @12:02PM (#12269335)
> Isn't the state machine approach the same as C#/.net async/await is using ? But the with the added convenience of the syntactic sugar ?

Similar in principle, but the implementation is different. Tasks in C# are more of an OO style instead of a FP style where they turn into an enum (sum type, if you want to get theoretical).

> 2 - no allocation : , does'nt the lambda closure need to be allocated somewhere ?

No, not in Rust (or in C++).

> from my understand the C++ approach is even more efficient in term of context switching and have the advantage of even less allocation.

No, you're asserting this without understanding Rust. It's not possible to get less than zero allocation.

Re:Zero-cost futures in Rust by soulbadguy on Thursday August 11, 2016 @12:11PM (#12269417)
Yes , i only have a simple understanding of rust memory model . So what happened when/if the future escapes/outlives the current stack/context ?
Re:Zero-cost futures in Rust by aturon on Thursday August 11, 2016 @12:19PM (#12269492)
To be clear, the issue here isn't so much stack vs heap, as much as how many heap allocations are happening.

In practice, you build up a really big combined future on the stack, which has all of the space needed for any state in its state machine, and then when the future is fired off (in a task -- one per connection), that entire thing is moved into the heap in one shot. Thus, generally, you do one big allocation up front per connection -- exactly the same as you'd do when writing state machines by hand.

The follow up posts will go into much more detail on this point :)

Re:Zero-cost futures in Rust by soulbadguy on Thursday August 11, 2016 @03:20PM (#12270916)
Fair enough, but op was quite misleading by saying that in rust closure don't need allocation.

So back to future vs coroutine , it seems that they the advantage in term of allocation simply because the context is not distroid when suspending/resuming coroutines

Re:Zero-cost futures in Rust by steveklabnik on Thursday August 11, 2016 @03:27PM (#12270967)

  let mut counter = 0;
  let mut increment = || { counter += 1; counter };

  increment();
  increment();
There is no heap allocation there.
Re:Zero-cost futures in Rust by saynsedit on Thursday August 11, 2016 @03:50PM (#12271110)
This is missing his point. "Increment" is called outside of its scope when the epoll triggers. If "counter" is stack allocated that would cause a segfault, so it must be heap allocated which is his point.
Re:Zero-cost futures in Rust by steveklabnik on Thursday August 11, 2016 @04:03PM (#12271205)
Well, they're two different points: closures in rust do not inherently have to heap allocate. But that also doesn't mean that they can _not_ be heap allocated either.

And in this example, it's not even really the closures that allocate: it's still one allocation, regardless of the number of closures.

Re:Zero-cost futures in Rust by soulbadguy on Thursday August 11, 2016 @05:56PM (#12271883)
No. they are not two different points. You are artificially restricting the argument, and saying that some parts are a different question by introducing this new idea of "inherent nature" of a closure in rust : Some humans have legs, some do not. Does it mean that having legs is not an "inherent" part of the human experience?

So to go back to the argument, we are trying to compare using a coroutine base approach vs using a future/state machine + closure approach. My point was that because a coroutine allows one to enter and leave a stack frame without destroying it, it can lead to less allocation and therefore be more efficient in those cases (and let's not even talk about the cost of the activation/deactivation of the stack frame)

Re:Zero-cost futures in Rust by steveklabnik on Thursday August 11, 2016 @06:23PM (#12272070)
To use your analogy, I feel like you're saying that only humans that have legs are human. I'm saying that they're all human.

This analogy is weird.

Furthermore, because closures aren't inherently boxed, there might not even be a stack frame to begin with. Closures are syntax sugar for a struct + a trait implementation on that struct, and said call is then very inline-able.

This is also where I'm getting at with the single invocation thing: with these futures, all of these closures are going to be inlined into one big state machine, which is then itself eventually put on the heap. But that heap allocation has nothing to do with the closure, and more to do with the future wanting to not tie its data down to a specific stack frame.

Re:Zero-cost futures in Rust by saynsedit on Thursday August 11, 2016 @07:44PM (#12272529)
I think this is becoming a semantic argument.

I think we all understand that Rust/C++ closures don't necessarily allocate memory. His point is simply that by using a rust closure to asynchronously handle an event a heap allocation must be done. Compare to the stack swapping method of classical coroutines, which wouldn't require a heap allocation.

I recognize that with Rust zero-cost futures, one big heap allocation is done for the entire futures combination and is roughly analogous to one big eager stack allocation for a coroutine.

Re:Zero-cost futures in Rust by steveklabnik on Thursday August 11, 2016 @07:53PM (#12272579)
Yes, I think so too. I just want to make sure the semantics are clear. I think your summary here is excellent.
Re:Zero-cost futures in Rust by tatterdemalion on Thursday August 11, 2016 @07:39PM (#12272498)
> Some humans have legs, some do not. Does it mean that having legs is not an "inherent" part of the human experience?

I mean - yes, that is what it means.

Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @06:01PM (#12271918)
You don't have to destroy the "stack frame" (by which I assume you mean the state retained between blocking operations) every time you enter and leave. Why do you think you need to?
Re:Zero-cost futures in Rust by saynsedit on Thursday August 11, 2016 @07:34PM (#12272469)
He's talking about the difference between swapping the stack pointer and executing a single "jump" instruction vs initiating a full function call (pushing argument(s), function prologue/epilogue)
Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @08:22PM (#12272711)
Calling a function isn't "allocation". And you don't just swap the stack pointer: you have to reload the register state. It's the same cost as a function call.
Re:Zero-cost futures in Rust by yazaddaruvala on Friday August 12, 2016 @05:49AM (#12274337)
May I ask, where do you assume a coroutine's stack lives?

i.e. In relation to program memory

Re:Zero-cost futures in Rust by soulbadguy on Friday August 12, 2016 @07:44AM (#12274779)
Not sure what you mean, but from my understating the coroutine e stacks are just allocated pieces of memory in the current address space ( might be stack allocated or heap allocated depending the the situation)
Re:Zero-cost futures in Rust by eddyb on Thursday August 11, 2016 @12:20PM (#12269501)
Compile error if it's holding on to any borrowed data on the stack - however, borrowing is only the default, if you move from a capture your closure will contain that value instead of reference to the value, and if you have "move" in front of your closure, all the captures are contained in the closure, so by boxing or by using -> impl Fn(...) you can safely return it.

If you need to hold onto data accessed from multiple locations, you'd need to use Rc<T> instead of keeping that data on the stack, which is closer to GC'd languages (and pretty similar to Swift, IIUC, except Rust is more explicit).

Re:Zero-cost futures in Rust by Rusky on Thursday August 11, 2016 @12:15PM (#12269454)
This is handled by the borrow checker, the same as anything else that might hold references to stack data - you get a compiler error and then can decide how to resolve the conflict, either by extending the lifetime of the referent (perhaps by boxing, or creating it earlier in the call stack), or by shrinking the lifetime of the future.
Re:Zero-cost futures in Rust by dbaupp on Thursday August 11, 2016 @12:16PM (#12269466)
It's an error if that would be unsafe, or else it compiles fine: the future compiles down to an enum/struct that stores everything inline and it can be returned/move up the stack/stored in global memory the same way that aggregate values returned without allocation (etc.) in C or C++.
Re:Zero-cost futures in Rust by steveklabnik on Thursday August 11, 2016 @12:16PM (#12269458)
From the post

  > There are essentially no imposed synchronization costs; if you want
  > to associate data that lives on your event loop and access it in a
  > single-threaded way from futures, we give you the tools to do so.
Re:Zero-cost futures in Rust by gpderetta on Thursday August 11, 2016 @05:18PM (#12271621)
C++ programmer here; re 3: the C++ coroutine proposals (there are multiple) are completely orthogonal to futures.

The closest things in the standard to these rust futures, is of course std::future, which currently is very limited as it even lacks chaining and composition. The Concurrency TR (which eventually will be added to the standard, but sadly not for C++17) does provide these features (and more); multiple implementations have been are available for some time (boost, HPX, Folly, and some compiler vendors).

The issues with std::future is that is far from zero cost, as it requires allocation (the control block is dynamically allocated), type erasure (the callback type is not part of the future) and synchronisation (as the promise can be fulfilled from another thread). Efficient implementations of std::future are possible but will never be as fast as what is shown in this rust library.

On the other hand the SeaStar C++ library (I'm in no way involved, just a fan) already provides pretty much the same zero cost futures with a very similar implementation as it doesn't try to follow the standard design.

Re:Zero-cost futures in Rust by soulbadguy on Thursday August 11, 2016 @06:08PM (#12271973)
Thanks your answer clarifies a lot. So my question was , as a mechanism to implement async operation how does C++ coroutine compares to this rust library. From my understanding the C++ committee is pushing for the introduction of coroutine specifically because any library base approach would have the limitation you mentioned. Is that a correct ?

You seems familiar with the C++ committee, do you know a good way to track a feature one is interested in ? I kinda lost track of the coroutine proposal after the 4 revision or Gor paper.

By the beside Visual studio, any other compiler has a working implementation of the current draft ?

Re:Zero-cost futures in Rust by gpderetta on Thursday August 11, 2016 @06:29PM (#12272105)
Coroutines would still run on top of std::future, it would be Just syntactic sugar to avoid callback hell via then chaining. In same case this might save some allocation as the callback would be a plain pointer to the control block, but that's about it.

I'm not part of the committee nor I attend meetings, but I follow the papers and the public online discussions.

Re:Zero-cost futures in Rust by soulbadguy on Thursday August 11, 2016 @06:39PM (#12272153)
From the last Gor paper, coroutine don't need to run on top of the std::future class. The type traits required allows for more computational patters (like generators, agent/actor, full coroutines) etc...
Re:Zero-cost futures in Rust by gpderetta on Thursday August 11, 2016 @07:31PM (#12272456)
No, but they are still designed to work well with std::futures and that's going to be the standard blessed way to do async IO.
Re:Zero-cost futures in Rust by Rusky on Thursday August 11, 2016 @12:06PM (#12269371)
In Rust, closures don't need to be boxed, because they're just bytes like everything else. In this case, they can be stored by-value as a generic type in the future itself, which can be stored on the stack.

The one time that you do tend to see closures get boxed is when returning them, because you can't write out their type. In the future you'll be able to get around that by using the `impl Trait` syntax mentioned in the post (https://github.com/rust-lang/rfcs/pull/1522).

Re:Zero-cost futures in Rust by steveklabnik on Thursday August 11, 2016 @12:00PM (#12269315)
1. I am not sure exactly how async/await is implemented, but I believe it is very similar. Some people are also working on implementing similar sugar in Rust, but it's not done yet.

2. Closures are on the stack, not the heap, by default, in Rust. If you don't see a Box, they're not heap allocated.

3. I agree!

Re:Zero-cost futures in Rust by moosingin3space on Thursday August 11, 2016 @12:03PM (#12269345)
Stack-allocated closures are one of my favorite features of Rust. They make it easy to write functional-style code that performs well.
Re:Zero-cost futures in Rust by indeyets on Friday August 12, 2016 @09:23AM (#12275392)
Re:Zero-cost futures in Rust by masklinn on Thursday August 11, 2016 @12:25PM (#12269544)
> 2 - no allocation : , does'nt the lambda closure need to be allocated somewhere ?

I expect it is embedded in the state machine structure. In Rust (and C++) lambdas/closures are "value types".

Re:Zero-cost futures in Rust by astn on Thursday August 11, 2016 @01:19PM (#12270040)
In C++ lambdas have limited size (about 3 machine words). If it's context is larger than that, the data may be allocated on heap (if compiler can't optimize that allocation out).
Re:Zero-cost futures in Rust by comex on Thursday August 11, 2016 @02:05PM (#12270407)
What you're talking about is std::function, a wrapper around raw lambda objects that performs dynamic dispatch. Since any lambda can be wrapped in std::function yet std::function, like all C++ types, must have a fixed size, it uses the heap as you say.

If you stick to the raw lambdas, however, the compiler knows the size and doesn't need to use the heap. Only problem is that their type can't be named. In C++, you can work around this with auto.

Without getting too much into it, Rust has a similar distinction between raw lambdas and Box<Fn(...)>. This library uses the former.

Re:Zero-cost futures in Rust by soulbadguy on Thursday August 11, 2016 @01:56PM (#12270343)
I though in c++ the lambda get created on the stack like any other object.
Re:Zero-cost futures in Rust by astn on Thursday August 11, 2016 @02:56PM (#12270751)
They do, but to actually use it, to pass it anywhere, you have to either wrap it in some other object (std::function, as comex pointed out) or use a template.