Re: [boost] future/packaged_task Sean Parent designn
On 24 Jun 2015 10:11 am, "Niall Douglas"
On 24 Jun 2015 at 1:09, Vicente J. Botet Escriba wrote:
Sean Parent suggests (See [1] Better Code: Concurrency) to remove the promise and left the packaged_task serve as single provider of a future. Both are created at once using the package factory.
I was at his talk at C++ Now.
The first thing which put me off his proposal was that he appears to be unaware of the highly optimised and very flexible de facto task concurrency system in C++ which has been around for years, and has just recently been standardised. It's called ASIO.
I also think packaged_task is overkill for creating futures. There are some times you really do just want a promise and nothing more.
There is no reason for a packaged task implementation to be more heavy weight than a promise. If the packaged task is [](T x) { return x; } then it is also semantically equivalent to promise<T>, just in a nicer interface. Bonus points it works beautifully with ASIO without any explicit support from ASIO itself.
There are even times you just want to create a future standalone, and for that we now have make_ready_future().
His future library doesn't include a shared_future as the future contains a value that can be observed several times.
If I remember rightly, internally his future has a shared_ptr, which really means he's proposing that future becomes a shared_future. I had doubts before until that part, but that killed his proposal for me. It's a non-starter as a practical design for my needs, but is still useful for ideas.
His implementation uses a shared ptr, but I think you could do without by deep copying the futures and internally chaining via then. This way you only pay the cost if you actually copy. I strongly believe that 'then' and other accessors should be non const so that concurrent calls *on the same future* need not be supported. -- gpd
On 24 June 2015 at 11:48, Giovanni Piero Deretta
On 24 Jun 2015 10:11 am, "Niall Douglas"
wrote: On 24 Jun 2015 at 1:09, Vicente J. Botet Escriba wrote:
Sean Parent suggests (See [1] Better Code: Concurrency) to remove the promise and left the packaged_task serve as single provider of a
future.
Both are created at once using the package factory.
I was at his talk at C++ Now.
The first thing which put me off his proposal was that he appears to be unaware of the highly optimised and very flexible de facto task concurrency system in C++ which has been around for years, and has just recently been standardised. It's called ASIO.
I also think packaged_task is overkill for creating futures. There are some times you really do just want a promise and nothing more.
There is no reason for a packaged task implementation to be more heavy weight than a promise. If the packaged task is [](T x) { return x; } then it is also semantically equivalent to promise<T>, just in a nicer interface. Bonus points it works beautifully with ASIO without any explicit support from ASIO itself.
I think this is a very important point. Of course the interaction is better with most existing code bases of which ASIO is merely one (important) example. It is clear that in at least some cases the packaged task implementation need not add overhead. Does anyone have any examples where one needs the additional abstraction for optimal performance?
There are even times you just want to create a future standalone, and for that we now have make_ready_future().
His future library doesn't include a shared_future as the future contains a value that can be observed several times.
If I remember rightly, internally his future has a shared_ptr, which really means he's proposing that future becomes a shared_future. I had doubts before until that part, but that killed his proposal for me. It's a non-starter as a practical design for my needs, but is still useful for ideas.
His implementation uses a shared ptr, but I think you could do without by deep copying the futures and internally chaining via then. This way you only pay the cost if you actually copy.
Is there any data about this trade-off? It seems that requiring a deep copy could, at least under some use-cases be substantial overhead. With a deep copy requirement are you prohibiting a wrapper that uses a shared_ptr? It seems more idiomatic to default to value-type semantics while providing flexibility to reduce copy/clone overhead.
I strongly believe that 'then' and other accessors should be non const so that concurrent calls *on the same future* need not be supported.
Is this a necessary logical result of the const-ness, or do you mean you desire the absence of const to be able to assert misuse? I'm wondering if we can define a desing contract such that some futures may be called multiple times while for others this is not allowed. This doesn't seem like it ought to be part of the future interface, but more a contract of the abstractions being operated upon by the future. Perhaps I'm not understanding a use-case?
-- gpd
Neil
On 24 Jun 2015 at 16:30, Neil Groves wrote:
There is no reason for a packaged task implementation to be more heavy weight than a promise. If the packaged task is [](T x) { return x; } then it is also semantically equivalent to promise<T>, just in a nicer interface. Bonus points it works beautifully with ASIO without any explicit support from ASIO itself.
I think this is a very important point. Of course the interaction is better with most existing code bases of which ASIO is merely one (important) example. It is clear that in at least some cases the packaged task implementation need not add overhead. Does anyone have any examples where one needs the additional abstraction for optimal performance?
Yes: https://boostgsoc13.github.io/boost.afio/doc/html/afio/reference/class es/enqueued_task_r___.html std::packaged_task has a very unhelpful design for real world programming. The ability to extract its its internally held promise, and optionally set it early within the task is exactly what afio::enqueued_task does. If I remember rightly, I added a good 30% to performance using this slightly enhanced packaged_task. For real world sized tasks this stuff matters.
His implementation uses a shared ptr, but I think you could do without by deep copying the futures and internally chaining via then. This way you only pay the cost if you actually copy.
Is there any data about this trade-off? It seems that requiring a deep copy could, at least under some use-cases be substantial overhead. With a deep copy requirement are you prohibiting a wrapper that uses a shared_ptr? It seems more idiomatic to default to value-type semantics while providing flexibility to reduce copy/clone overhead.
This is effectively the same as a linked list, and that is terrible for performance after a certain depth of chains. You're better off allocating memory and using shared_ptr after a certain depth.
I strongly believe that 'then' and other accessors should be non const so that concurrent calls *on the same future* need not be supported.
Is this a necessary logical result of the const-ness, or do you mean you desire the absence of const to be able to assert misuse? I'm wondering if we can define a desing contract such that some futures may be called multiple times while for others this is not allowed. This doesn't seem like it ought to be part of the future interface, but more a contract of the abstractions being operated upon by the future. Perhaps I'm not understanding a use-case?
future<T> returns a one-shot value. Therefore its .then() is also one-shot because it equals consuming any future value. This makes total sense. shared_future<T> returns its value as often as desired. In that circumstance it is implied that more than one thread could fetch a value. It therefore needs to be thread safe. AFIO uses a lot of shared_future<T> currently, and it's all done under the assumption that it's thread safe. I'd assume so does any other code using shared_future<T>. I think the ship has sailed on that design choice. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 24 Jun 2015 4:48 pm, "Niall Douglas"
On 24 Jun 2015 at 16:30, Neil Groves wrote:
There is no reason for a packaged task implementation to be more heavy weight than a promise. If the packaged task is [](T x) { return x; }
then
it is also semantically equivalent to promise<T>, just in a nicer interface. Bonus points it works beautifully with ASIO without any explicit support from ASIO itself.
I think this is a very important point. Of course the interaction is better with most existing code bases of which ASIO is merely one (important) example. It is clear that in at least some cases the packaged task implementation need not add overhead. Does anyone have any examples where one needs the additional abstraction for optimal performance?
Yes: https://boostgsoc13.github.io/boost.afio/doc/html/afio/reference/class es/enqueued_task_r___.html
std::packaged_task has a very unhelpful design for real world programming. The ability to extract its its internally held promise, and optionally set it early within the task is exactly what afio::enqueued_task does.
A Packaged task is for all intent and purposes a promise. How exactly would you use the underlying promise and in which scenarios? -- gpd
On 24 Jun 2015 4:30 pm, "Neil Groves"
Hi Neal, long time no see..
On 24 June 2015 at 11:48, Giovanni Piero Deretta
wrote: On 24 Jun 2015 10:11 am, "Niall Douglas"
wrote: On 24 Jun 2015 at 1:09, Vicente J. Botet Escriba wrote:
There are even times you just want to create a future standalone, and for that we now have make_ready_future().
His future library doesn't include a shared_future as the future contains a value that can be observed several times.
If I remember rightly, internally his future has a shared_ptr, which really means he's proposing that future becomes a shared_future. I had doubts before until that part, but that killed his proposal for me. It's a non-starter as a practical design for my needs, but is still useful for ideas.
His implementation uses a shared ptr, but I think you could do without
by
deep copying the futures and internally chaining via then. This way you only pay the cost if you actually copy.
Is there any data about this trade-off? It seems that requiring a deep copy could, at least under some use-cases be substantial overhead. With a deep copy requirement are you prohibiting a wrapper that uses a shared_ptr? It seems more idiomatic to default to value-type semantics while providing flexibility to reduce copy/clone overhead.
No data yet, need to play with it a bit... Anyways, I think it might be possible to reference count the shared_state and not pay for atomic inc and decs as long as there is only one future. This way only if you actually copy you pay for it.
I strongly believe that 'then' and other accessors should be non const
so
that concurrent calls *on the same future* need not be supported.
Is this a necessary logical result of the const-ness, or do you mean you desire the absence of const to be able to assert misuse?
The former. Any const function is supposed to be concurrency safe, which means that you need to protect the internal then list with a lock which I would prefer not to do. -- gpd
participants (3)
-
Giovanni Piero Deretta
-
Neil Groves
-
Niall Douglas