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.
> > proposed await keyword can be adapted to handle arbitrary future types
> > a user supplied trait. We tried that with hpx::future and it seems to
> > fine (once they fix a compiler bug which prevented us from doing large
> > 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
> 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
> > the overheads introduced by the underlying threading system we're fine.
> > for std::future and boost::future this is definitely the case as both
> > tied to kernel-threads. In HPX this is a bigger problem as the overheads
> > introduced by futures are comparable with those of the underlying
> > system (sub-microsecond). However in our experience this is solvable
> > like special allocators for the shared state and using intrusive_ptr for
> > come to mind).
> I think a one size fits all future is a fundamentally flawed
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-
> > for the non-exceptional case. And I personally don't care if the
> > 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());
> return f.get();
> ... can be optimised by the compiler into:
> _Z5test1v: # @_Z5test1v
> # BB#0: # %_ZN7promiseIiJEED2Ev.exit2
> movl $5, %eax
> 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
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.
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk