Boost logo

Boost :

Subject: Re: [boost] Futures (was: Re: [compute] Some remarks)
From: Hartmut Kaiser (hartmut.kaiser_at_[hidden])
Date: 2015-01-05 08:52:16


> > > 1. They tie your code into "future islands" which are fundamentally
> > > incommensurate with all code which doesn't use the same future as
> > > your code. Try mixing code using boost::future and std::future for
> > > example, it's a nightmare of too easy to be racy and unmaintainable
> > > mess code. If Compute provided a boost::compute::future, it would yet
> > > another new future island, and I'm not sure that's wise design.
> >
> > The easiest way to deal with this is to introduce a Future concept and
> > implement everything in terms of it. A solid set of traits/concepts-lite
> > should cover that.
>
> I don't think it's that easy because really it comes down to
> commonality of kernel wait object, or rather, whether one has access
> to the true underlying kernel wait object or not.

Relying on the kernel to do threading is plain slow - at least with what we
usually have today.

> For example, right now can boost::wait_all() ever consume
> std::futures? I suspect not because the HANDLE on Windows or the
> futex on Linux is rather hard to get at.

You don't need to - as long as the interfaces exposed by that concept are
powerful enough to handle things, that is.

> > A good example is the proposed await 2.0 (see N4134:
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4134.pdf), btw.
> The
> > proposed await keyword can be adapted to handle arbitrary future types
> using
> > a user supplied trait. We tried that with hpx::future and it seems to
> work
> > fine (once they fix a compiler bug which prevented us from doing large
> scale
> > tests).
>
> N4134 presents excellent progress, apart from one major thing I
> disliked about it: I deeply dislike magic compiler tricks which fold
> the allocation of shared state by a future promise onto the stack.

You don't have to do that, it's just one possible way of implementing the
trait.

> Far, far better to fix the present future promise API to stop
> requiring shared state and therefore memory allocation at all.

Not sure you can implement a shared_future without a shared state.

> > > 2. Every time you touch them with change you unavoidably spend
> > > thousands of CPU cycles due to going through the memory allocator and
> > > (effectively) the internal shared_ptr. This makes using futures for a
> > > single SHA round, for example, a poor design despite how nice and
> > > clean it is.
> >
> > As long as the overheads of managing the future itself are much smaller
> than
> > the overheads introduced by the underlying threading system we're fine.
> And
> > for std::future and boost::future this is definitely the case as both
> are
> > tied to kernel-threads. In HPX this is a bigger problem as the overheads
> > introduced by futures are comparable with those of the underlying
> threading
> > system (sub-microsecond). However in our experience this is solvable
> (things
> > like special allocators for the shared state and using intrusive_ptr for
> it
> > come to mind).
>
> I think a one size fits all future is a fundamentally flawed
> approach.

Did I say 'one size fits all'? I don't think so.

> > > 3. They force you to deal with exceptions even where that is not
> > > appropriate, and internally most implementations will do one or more
> > > internal throw-catches which if the exception type has a vtable, can
> > > be particularly slow.
> >
> > The implementations will throw only if there is an error. This is a no-
> issue
> > for the non-exceptional case. And I personally don't care if the
> exceptional
> > case is slow (involves things like logging anyways, etc.).
>
> That's not the problem. The problem is that the compiler cannot know
> if no exception will ever be generated and therefore has to generate
> the opcodes anyway. What I'm really asking for is a "noexcept future"
> such that this sequence:
>
> promise<int> p;
> auto f(p.get_future());
> p.set_value(5);
> return f.get();
>
> ... can be optimised by the compiler into:
>
> _Z5test1v: # @_Z5test1v
> .cfi_startproc
> # BB#0: # %_ZN7promiseIiJEED2Ev.exit2
> movl $5, %eax
> ret
>
> Obviously this is an unrealistic use case, but my point is that the
> compiler should be capable of such a reduction because the correct
> design of future promise wouldn't get in the way.
>
> My earlier proposal for non-allocating future promise doesn't even
> transport a value it's so basic (hence "basic_future" and
> "basic_promise"). It pushes that responsibility onto something like
> expected<T, E> where if the programmer chooses, E can be a
> std::exception_ptr in which case you get exception capable futures.
> Or or E can be a std::error_code, in which case the future is now
> noexcept and the compiler can completely elide all related exception
> handling machinery.
>
> Obviously one could use an expected<expected<T, error_code>,
> exception_ptr> too. In fact, this is exactly what I need for AFIO and
> is why I started down this rabbit hole at all. Personally speaking, I
> have better things to be doing than working on futures, but right now
> they are a major showstopper for me, particularly for efficient SHA
> hashing.

Sure, let's do that. It will make futures more efficient, however does not
solve you initial concern of 'future islands' as it introduces yet another
future type into the mix.

> > > To that end, the non-allocating basic_future toolkit I proposed on
> > > this list before Christmas I think has the best chance of "fixing"
> > > futures. Each programmer can roll their own future type, with
> > > optional amounts of interoperability and composure with other future
> > > islands. Then a future type lightweight enough for a SHA round is
> > > possible, as is some big thick future type providing STL future
> > > semantics or composure with many other custom future types. One also
> > > gains most of the (static) benefits of ASIO's async_result, but one
> > > still has ABI stability.
> >
> > Non-allocating futures are a step in the right direction. But even those
> > require to solve some of the problems you mentioned. Otherwise they will
> > make the issue of having future-islands just a bit bigger...
>
> Eliminating future islands is, I suspect, not something the C++
> community can entirely do alone. We are, as a minimum, going to have
> to petition POSIX for improved runtime support. We probably ought to
> have our ducks in a row before that though.

*shiver*

I'd rather embrace future islands as we will not be able to solve this.
Let's use the facilities available to us (C++!) and solve it inside the
language/library.

Regards Hartmut
---------------
http://boost-spirit.com
http://stellar.cct.lsu.edu


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk