From 7dfd7de8cda6650ecdbb2be0c47922ae231feed6 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 9 Dec 2014 01:39:45 +0200 Subject: [PATCH] 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. --- core/future.hh | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/core/future.hh b/core/future.hh index b31bde3856..e458f4c939 100644 --- a/core/future.hh +++ b/core/future.hh @@ -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 struct future_task final : task, future_state { Func _func; @@ -513,6 +583,16 @@ public: friend future 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 inline future