BLI: optimize constructing new virtual array

Differential Revision: https://developer.blender.org/D14745
This commit is contained in:
Jacques Lucke 2022-04-25 11:51:22 +02:00
parent c63d64a2ce
commit a2d32960b4
5 changed files with 78 additions and 70 deletions

View File

@ -31,55 +31,37 @@ template<typename ExtraInfo> struct AnyTypeInfo {
void (*destruct)(void *src);
const void *(*get)(const void *src);
ExtraInfo extra_info;
/**
* Used when #T is stored directly in the inline buffer of the #Any.
*/
template<typename T> static const AnyTypeInfo &get_for_inline()
{
static AnyTypeInfo funcs = {[](void *dst, const void *src) { new (dst) T(*(const T *)src); },
[](void *dst, void *src) { new (dst) T(std::move(*(T *)src)); },
[](void *src) { ((T *)src)->~T(); },
[](const void *src) { return src; },
ExtraInfo::template get<T>()};
return funcs;
}
/**
* Used when #T can't be stored directly in the inline buffer and is stored in a #std::unique_ptr
* instead. In this scenario, the #std::unique_ptr is stored in the inline buffer.
*/
template<typename T> static const AnyTypeInfo &get_for_unique_ptr()
{
using Ptr = std::unique_ptr<T>;
static AnyTypeInfo funcs = {
[](void *dst, const void *src) { new (dst) Ptr(new T(**(const Ptr *)src)); },
[](void *dst, void *src) { new (dst) Ptr(new T(std::move(**(Ptr *)src))); },
[](void *src) { ((Ptr *)src)->~Ptr(); },
[](const void *src) -> const void * { return &**(const Ptr *)src; },
ExtraInfo::template get<T>()};
return funcs;
}
/**
* Used when the #Any does not contain any type currently.
*/
static const AnyTypeInfo &get_for_empty()
{
static AnyTypeInfo funcs = {[](void *UNUSED(dst), const void *UNUSED(src)) {},
[](void *UNUSED(dst), void *UNUSED(src)) {},
[](void *UNUSED(src)) {},
[](const void *UNUSED(src)) -> const void * { return nullptr; },
ExtraInfo{}};
return funcs;
}
};
/**
* Used when #T is stored directly in the inline buffer of the #Any.
*/
template<typename ExtraInfo, typename T>
static constexpr AnyTypeInfo<ExtraInfo> info_for_inline = {
[](void *dst, const void *src) { new (dst) T(*(const T *)src); },
[](void *dst, void *src) { new (dst) T(std::move(*(T *)src)); },
[](void *src) { ((T *)src)->~T(); },
[](const void *src) { return src; },
ExtraInfo::template get<T>()};
/**
* Used when #T can't be stored directly in the inline buffer and is stored in a #std::unique_ptr
* instead. In this scenario, the #std::unique_ptr is stored in the inline buffer.
*/
template<typename T> using Ptr = std::unique_ptr<T>;
template<typename ExtraInfo, typename T>
static constexpr AnyTypeInfo<ExtraInfo> info_for_unique_ptr = {
[](void *dst, const void *src) { new (dst) Ptr<T>(new T(**(const Ptr<T> *)src)); },
[](void *dst, void *src) { new (dst) Ptr<T>(new T(std::move(**(Ptr<T> *)src))); },
[](void *src) { ((Ptr<T> *)src)->~Ptr<T>(); },
[](const void *src) -> const void * { return &**(const Ptr<T> *)src; },
ExtraInfo::template get<T>()};
/**
* Dummy extra info that is used when no additional type information should be stored in the #Any.
*/
struct NoExtraInfo {
template<typename T> static NoExtraInfo get()
template<typename T> static constexpr NoExtraInfo get()
{
return {};
}
@ -119,8 +101,9 @@ class Any {
/**
* Information about the type that is currently stored.
* This is null when the #Any does not contain a value.
*/
const Info *info_ = &Info::get_for_empty();
const Info *info_ = nullptr;
public:
/** Only copy constructible types can be stored in #Any. */
@ -148,10 +131,10 @@ class Any {
using DecayT = std::decay_t<T>;
static_assert(is_allowed_v<DecayT>);
if constexpr (is_inline_v<DecayT>) {
return Info::template get_for_inline<DecayT>();
return detail::template info_for_inline<RealExtraInfo, DecayT>;
}
else {
return Info::template get_for_unique_ptr<DecayT>();
return detail::template info_for_unique_ptr<RealExtraInfo, DecayT>;
}
}
@ -160,7 +143,9 @@ class Any {
Any(const Any &other) : info_(other.info_)
{
info_->copy_construct(&buffer_, &other.buffer_);
if (info_ != nullptr) {
info_->copy_construct(&buffer_, &other.buffer_);
}
}
/**
@ -169,7 +154,9 @@ class Any {
*/
Any(Any &&other) noexcept : info_(other.info_)
{
info_->move_construct(&buffer_, &other.buffer_);
if (info_ != nullptr) {
info_->move_construct(&buffer_, &other.buffer_);
}
}
/**
@ -178,18 +165,7 @@ class Any {
*/
template<typename T, typename... Args> explicit Any(std::in_place_type_t<T>, Args &&...args)
{
using DecayT = std::decay_t<T>;
static_assert(is_allowed_v<DecayT>);
info_ = &this->template get_info<DecayT>();
if constexpr (is_inline_v<DecayT>) {
/* Construct the value directly in the inline buffer. */
new (&buffer_) DecayT(std::forward<Args>(args)...);
}
else {
/* Construct the value in a new allocation and store a #std::unique_ptr to it in the inline
* buffer. */
new (&buffer_) std::unique_ptr<DecayT>(new DecayT(std::forward<Args>(args)...));
}
this->emplace_on_empty<T>(std::forward<Args>(args)...);
}
/**
@ -202,7 +178,9 @@ class Any {
~Any()
{
info_->destruct(&buffer_);
if (info_ != nullptr) {
info_->destruct(&buffer_);
}
}
/**
@ -234,8 +212,10 @@ class Any {
/** Destruct any existing value to make it empty. */
void reset()
{
info_->destruct(&buffer_);
info_ = &Info::get_for_empty();
if (info_ != nullptr) {
info_->destruct(&buffer_);
}
info_ = nullptr;
}
operator bool() const
@ -245,7 +225,7 @@ class Any {
bool has_value() const
{
return info_ != &Info::get_for_empty();
return info_ != nullptr;
}
template<typename T, typename... Args> std::decay_t<T> &emplace(Args &&...args)
@ -255,6 +235,26 @@ class Any {
return this->get<T>();
}
template<typename T, typename... Args> std::decay_t<T> &emplace_on_empty(Args &&...args)
{
BLI_assert(!this->has_value());
using DecayT = std::decay_t<T>;
static_assert(is_allowed_v<DecayT>);
info_ = &this->template get_info<DecayT>();
if constexpr (is_inline_v<DecayT>) {
/* Construct the value directly in the inline buffer. */
DecayT *stored_value = new (&buffer_) DecayT(std::forward<Args>(args)...);
return *stored_value;
}
else {
/* Construct the value in a new allocation and store a #std::unique_ptr to it in the inline
* buffer. */
std::unique_ptr<DecayT> *stored_value = new (&buffer_)
std::unique_ptr<DecayT>(new DecayT(std::forward<Args>(args)...));
return **stored_value;
}
}
/** Return true when the value that is currently stored is a #T. */
template<typename T> bool is() const
{
@ -264,12 +264,14 @@ class Any {
/** Get a pointer to the stored value. */
void *get()
{
BLI_assert(info_ != nullptr);
return const_cast<void *>(info_->get(&buffer_));
}
/** Get a pointer to the stored value. */
const void *get() const
{
BLI_assert(info_ != nullptr);
return info_->get(&buffer_);
}
@ -298,6 +300,7 @@ class Any {
*/
const RealExtraInfo &extra_info() const
{
BLI_assert(info_ != nullptr);
return info_->extra_info;
}
};

View File

@ -83,7 +83,7 @@ struct GVArrayAnyExtraInfo {
const GVArrayImpl *(*get_varray)(const void *buffer) =
[](const void *UNUSED(buffer)) -> const GVArrayImpl * { return nullptr; };
template<typename StorageT> static GVArrayAnyExtraInfo get();
template<typename StorageT> static constexpr GVArrayAnyExtraInfo get();
};
} // namespace detail
@ -810,7 +810,7 @@ inline bool GVArrayCommon::is_empty() const
* \{ */
namespace detail {
template<typename StorageT> inline GVArrayAnyExtraInfo GVArrayAnyExtraInfo::get()
template<typename StorageT> constexpr GVArrayAnyExtraInfo GVArrayAnyExtraInfo::get()
{
static_assert(std::is_base_of_v<GVArrayImpl, StorageT> ||
is_same_any_v<StorageT, const GVArrayImpl *, std::shared_ptr<const GVArrayImpl>>);

View File

@ -564,10 +564,9 @@ template<typename T> struct VArrayAnyExtraInfo {
/**
* Gets the virtual array that is stored at the given pointer.
*/
const VArrayImpl<T> *(*get_varray)(const void *buffer) =
[](const void *UNUSED(buffer)) -> const VArrayImpl<T> * { return nullptr; };
const VArrayImpl<T> *(*get_varray)(const void *buffer);
template<typename StorageT> static VArrayAnyExtraInfo get()
template<typename StorageT> static constexpr VArrayAnyExtraInfo get()
{
/* These are the only allowed types in the #Any. */
static_assert(
@ -711,6 +710,9 @@ template<typename T> class VArrayCommon {
* null. */
const VArrayImpl<T> *impl_from_storage() const
{
if (!storage_.has_value()) {
return nullptr;
}
return storage_.extra_info().get_varray(storage_.get());
}

View File

@ -643,6 +643,9 @@ void GVArrayCommon::get_internal_single_to_uninitialized(void *r_value) const
const GVArrayImpl *GVArrayCommon::impl_from_storage() const
{
if (!storage_.has_value()) {
return nullptr;
}
return storage_.extra_info().get_varray(storage_.get());
}

View File

@ -90,7 +90,7 @@ TEST(any, AssignAny)
struct ExtraSizeInfo {
size_t size;
template<typename T> static ExtraSizeInfo get()
template<typename T> static constexpr ExtraSizeInfo get()
{
return {sizeof(T)};
}