future: optimize data-less future<>

A future that does not carry any data (future<>) and its sibling (promise<>)
are heavily used in the code.  We can optimize them by overlaying the
future's payload, which in this case can only be an std::exception_ptr,
with the future state, as a pointer and an enum have disjoint values.

This of course depends on std::exception_ptr being implemented as a pointer,
but as it happens, it is.

With this, sizeof(future<>) is reduced from 24 bytes to 16 bytes.
This commit is contained in:
Avi Kivity
2014-12-09 01:39:45 +02:00
parent 20acb6db9c
commit 7dfd7de8cd

View File

@@ -168,6 +168,76 @@ struct future_state {
}
};
// Specialize future_state<> to overlap the state enum with the exception, as there
// is no value to hold.
//
// Assumes std::exception_ptr is really a pointer.
template <>
struct future_state<> {
static_assert(sizeof(std::exception_ptr) == sizeof(void*), "exception_ptr not a pointer");
static constexpr const bool move_noexcept = true;
static constexpr const bool copy_noexcept = true;
enum class state : uintptr_t {
invalid = 0,
future = 1,
result = 2,
exception_min = 3, // or anything greater
};
union any {
any() { st = state::future; }
~any() {}
state st;
std::exception_ptr ex;
} _u;
future_state() noexcept {}
future_state(future_state&& x) noexcept {
if (x._u.st < state::exception_min) {
_u.st = x._u.st;
} else {
new (&_u.ex) std::exception_ptr(std::move(x._u.ex));
}
}
~future_state() noexcept {
if (_u.st >= state::exception_min) {
_u.ex.~exception_ptr();
}
}
future_state& operator=(future_state&& x) noexcept {
if (this != &x) {
this->~future_state();
new (this) future_state(std::move(x));
}
return *this;
}
bool available() const noexcept { return _u.st == state::result || _u.st >= state::exception_min; }
bool failed() const noexcept { return _u.st >= state::exception_min; }
void set(const std::tuple<>& value) noexcept {
assert(_u.st == state::future);
_u.st = state::result;
}
void set(std::tuple<>&& value) noexcept {
assert(_u.st == state::future);
_u.st = state::result;
}
void set() {
assert(_u.st == state::future);
_u.st = state::result;
}
void set_exception(std::exception_ptr ex) noexcept {
assert(_u.st == state::future);
new (&_u.ex) std::exception_ptr(ex);
assert(_u.st >= state::exception_min);
}
std::tuple<> get() {
assert(_u.st != state::future);
if (_u.st >= state::exception_min) {
std::rethrow_exception(_u.ex);
}
return {};
}
void forward_to(promise<>& pr) noexcept;
};
template <typename Func, typename... T>
struct future_task final : task, future_state<T...> {
Func _func;
@@ -513,6 +583,16 @@ public:
friend future<U...> make_exception_future(Exception&& ex) noexcept;
};
inline
void future_state<>::forward_to(promise<>& pr) noexcept {
assert(_u.st != state::future && _u.st != state::invalid);
if (_u.st >= state::exception_min) {
pr.set_exception(_u.ex);
} else {
pr.set_value(std::tuple<>());
}
}
template <typename... T>
inline
future<T...>