BLI: improve support for trivial virtual arrays
This commits reduces the number of function calls through function pointers in `blender::Any` when the stored type is trivial. Furthermore, this implements marks some classes as trivial, which we know are trivial but the compiler does not (the standard currently says that any class with a virtual destructor is non-trivial). Under some circumstances we know that final child classes are trivial though. This allows for some optimizations. Also see https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1077r0.html.
This commit is contained in:
parent
3237c6dbe8
commit
22fc0cbd69
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_memory_utils.hh"
|
||||
|
@ -26,6 +27,7 @@ namespace detail {
|
|||
* Additional type specific #ExtraInfo can be embedded here as well.
|
||||
*/
|
||||
template<typename ExtraInfo> struct AnyTypeInfo {
|
||||
/* The pointers are allowed to be null, which means that the implementation is trivial. */
|
||||
void (*copy_construct)(void *dst, const void *src);
|
||||
void (*move_construct)(void *dst, void *src);
|
||||
void (*destruct)(void *src);
|
||||
|
@ -38,10 +40,15 @@ template<typename ExtraInfo> struct AnyTypeInfo {
|
|||
*/
|
||||
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) { std::destroy_at(((T *)src)); },
|
||||
[](const void *src) { return src; },
|
||||
is_trivially_copy_constructible_extended_v<T> ?
|
||||
nullptr :
|
||||
+[](void *dst, const void *src) { new (dst) T(*(const T *)src); },
|
||||
is_trivially_move_constructible_extended_v<T> ?
|
||||
nullptr :
|
||||
+[](void *dst, void *src) { new (dst) T(std::move(*(T *)src)); },
|
||||
is_trivially_destructible_extended_v<T> ? nullptr :
|
||||
+[](void *src) { std::destroy_at(((T *)src)); },
|
||||
nullptr,
|
||||
ExtraInfo::template get<T>()};
|
||||
|
||||
/**
|
||||
|
@ -92,12 +99,14 @@ class Any {
|
|||
using RealExtraInfo =
|
||||
std::conditional_t<std::is_void_v<ExtraInfo>, detail::NoExtraInfo, ExtraInfo>;
|
||||
using Info = detail::AnyTypeInfo<RealExtraInfo>;
|
||||
static constexpr size_t RealInlineBufferCapacity = std::max(InlineBufferCapacity,
|
||||
sizeof(std::unique_ptr<int>));
|
||||
|
||||
/**
|
||||
* Inline buffer that either contains nothing, the stored value directly, or a #std::unique_ptr
|
||||
* to the value.
|
||||
*/
|
||||
AlignedBuffer<std::max(InlineBufferCapacity, sizeof(std::unique_ptr<int>)), Alignment> buffer_{};
|
||||
AlignedBuffer<RealInlineBufferCapacity, Alignment> buffer_{};
|
||||
|
||||
/**
|
||||
* Information about the type that is currently stored.
|
||||
|
@ -144,7 +153,12 @@ class Any {
|
|||
Any(const Any &other) : info_(other.info_)
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
info_->copy_construct(&buffer_, &other.buffer_);
|
||||
if (info_->copy_construct != nullptr) {
|
||||
info_->copy_construct(&buffer_, &other.buffer_);
|
||||
}
|
||||
else {
|
||||
memcpy(&buffer_, &other.buffer_, RealInlineBufferCapacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,7 +169,12 @@ class Any {
|
|||
Any(Any &&other) noexcept : info_(other.info_)
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
info_->move_construct(&buffer_, &other.buffer_);
|
||||
if (info_->move_construct != nullptr) {
|
||||
info_->move_construct(&buffer_, &other.buffer_);
|
||||
}
|
||||
else {
|
||||
memcpy(&buffer_, &other.buffer_, RealInlineBufferCapacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,7 +198,9 @@ class Any {
|
|||
~Any()
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
info_->destruct(&buffer_);
|
||||
if (info_->destruct != nullptr) {
|
||||
info_->destruct(&buffer_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +234,9 @@ class Any {
|
|||
void reset()
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
info_->destruct(&buffer_);
|
||||
if (info_->destruct != nullptr) {
|
||||
info_->destruct(&buffer_);
|
||||
}
|
||||
}
|
||||
info_ = nullptr;
|
||||
}
|
||||
|
@ -265,14 +288,20 @@ class Any {
|
|||
void *get()
|
||||
{
|
||||
BLI_assert(info_ != nullptr);
|
||||
return const_cast<void *>(info_->get(&buffer_));
|
||||
if (info_->get != nullptr) {
|
||||
return const_cast<void *>(info_->get(&buffer_));
|
||||
}
|
||||
return &buffer_;
|
||||
}
|
||||
|
||||
/** Get a pointer to the stored value. */
|
||||
const void *get() const
|
||||
{
|
||||
BLI_assert(info_ != nullptr);
|
||||
return info_->get(&buffer_);
|
||||
if (info_->get != nullptr) {
|
||||
return info_->get(&buffer_);
|
||||
}
|
||||
return &buffer_;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -600,6 +600,8 @@ class GVArrayImpl_For_GSpan_final final : public GVArrayImpl_For_GSpan {
|
|||
CommonVArrayInfo common_info() const override;
|
||||
};
|
||||
|
||||
template<> inline constexpr bool is_trivial_extended_v<GVArrayImpl_For_GSpan_final> = true;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -638,6 +640,9 @@ class GVArrayImpl_For_SingleValueRef_final final : public GVArrayImpl_For_Single
|
|||
CommonVArrayInfo common_info() const override;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline constexpr bool is_trivial_extended_v<GVArrayImpl_For_SingleValueRef_final> = true;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -18,6 +18,21 @@
|
|||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Under some circumstances #std::is_trivial_v<T> is false even though we know that the type is
|
||||
* actually trivial. Using that extra knowledge allows for some optimizations.
|
||||
*/
|
||||
template<typename T> inline constexpr bool is_trivial_extended_v = std::is_trivial_v<T>;
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivially_destructible_extended_v = is_trivial_extended_v<T> ||
|
||||
std::is_trivially_destructible_v<T>;
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivially_copy_constructible_extended_v =
|
||||
is_trivial_extended_v<T> || std::is_trivially_copy_constructible_v<T>;
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivially_move_constructible_extended_v =
|
||||
is_trivial_extended_v<T> || std::is_trivially_move_constructible_v<T>;
|
||||
|
||||
/**
|
||||
* Call the destructor on n consecutive values. For trivially destructible types, this does
|
||||
* nothing.
|
||||
|
@ -38,7 +53,7 @@ template<typename T> void destruct_n(T *ptr, int64_t n)
|
|||
|
||||
/* This is not strictly necessary, because the loop below will be optimized away anyway. It is
|
||||
* nice to make behavior this explicitly, though. */
|
||||
if (std::is_trivially_destructible_v<T>) {
|
||||
if (is_trivially_destructible_extended_v<T>) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -319,6 +319,9 @@ template<typename T> class VArrayImpl_For_Span_final final : public VArrayImpl_F
|
|||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Span_final<T>> = true;
|
||||
|
||||
/**
|
||||
* A variant of `VArrayImpl_For_Span` that owns the underlying data.
|
||||
* The `Container` type has to implement a `size()` and `data()` method.
|
||||
|
@ -379,6 +382,9 @@ template<typename T> class VArrayImpl_For_Single final : public VArrayImpl<T> {
|
|||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Single<T>> = is_trivial_extended_v<T>;
|
||||
|
||||
/**
|
||||
* This class makes it easy to create a virtual array for an existing function or lambda. The
|
||||
* `GetFunc` should take a single `index` argument and return the value at that index.
|
||||
|
@ -522,6 +528,13 @@ class VArrayImpl_For_DerivedSpan final : public VMutableArrayImpl<ElemT> {
|
|||
}
|
||||
};
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, ElemT)>
|
||||
inline constexpr bool
|
||||
is_trivial_extended_v<VArrayImpl_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> = true;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue