1
1

BLI: use no_unique_address attribute

Even though the `no_unique_address` attribute has only been standardized
in C++20, compilers seem to support it with C++17 already. This attribute
allows reducing the memory footprint of structs which have empty types as
data members (usually that is an allocator or inline buffer in Blender).
Previously, one had to use the empty base optimization to achieve the same
effect, which requires a lot of boilerplate code.

The types that benefit from this the most are `Vector` and `Array`, which
usually become 8 bytes smaller. All types which use these core data structures
get smaller as well of course.

Differential Revision: https://developer.blender.org/D14993
This commit is contained in:
2022-05-25 16:28:07 +02:00
parent f381c31ac6
commit a337e7738f
11 changed files with 42 additions and 23 deletions

View File

@@ -1756,6 +1756,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC")
"/wd4828" # The file contains a character that is illegal
"/wd4996" # identifier was declared deprecated
"/wd4661" # no suitable definition provided for explicit template instantiation request
"/wd4848" # 'no_unique_address' is a vendor extension in C++17
# errors:
"/we4013" # 'function' undefined; assuming extern returning int
"/we4133" # incompatible pointer types

View File

@@ -64,10 +64,10 @@ class Array {
int64_t size_;
/** Used for allocations when the inline buffer is too small. */
Allocator allocator_;
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
/** A placeholder buffer that will remain uninitialized until it is used. */
TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
public:
/**

View File

@@ -33,7 +33,7 @@ class GArray {
void *data_ = nullptr;
int64_t size_ = 0;
Allocator allocator_;
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
public:
/**

View File

@@ -18,7 +18,7 @@ namespace blender {
template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopyable, NonMovable {
private:
Allocator allocator_;
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
Vector<void *> owned_buffers_;
Vector<Span<char>> unused_borrowed_buffers_;

View File

@@ -130,10 +130,10 @@ class Map {
uint64_t slot_mask_;
/** This is called to hash incoming keys. */
Hash hash_;
BLI_NO_UNIQUE_ADDRESS Hash hash_;
/** This is called to check equality of two keys. */
IsEqual is_equal_;
BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_;
/** The max load factor is 1/2 = 50% by default. */
#define LOAD_FACTOR 1, 2

View File

@@ -317,30 +317,36 @@ template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddr
* An `AlignedBuffer` is a byte array with at least the given size and alignment. The buffer will
* not be initialized by the default constructor.
*/
template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer {
private:
/* Don't create an empty array. This causes problems with some compilers. */
char buffer_[(Size > 0) ? Size : 1];
template<size_t Size, size_t Alignment> class AlignedBuffer {
struct Empty {
};
struct alignas(Alignment) Sized {
/* Don't create an empty array. This causes problems with some compilers. */
std::byte buffer_[Size > 0 ? Size : 1];
};
using BufferType = std::conditional_t<Size == 0, Empty, Sized>;
BLI_NO_UNIQUE_ADDRESS BufferType buffer_;
public:
operator void *()
{
return buffer_;
return this;
}
operator const void *() const
{
return buffer_;
return this;
}
void *ptr()
{
return buffer_;
return this;
}
const void *ptr() const
{
return buffer_;
return this;
}
};
@@ -351,7 +357,7 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer {
*/
template<typename T, int64_t Size = 1> class TypedBuffer {
private:
AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_;
BLI_NO_UNIQUE_ADDRESS AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_;
public:
operator T *()

View File

@@ -136,10 +136,10 @@ class Set {
uint64_t slot_mask_;
/** This is called to hash incoming keys. */
Hash hash_;
BLI_NO_UNIQUE_ADDRESS Hash hash_;
/** This is called to check equality of two keys. */
IsEqual is_equal_;
BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_;
/** The max load factor is 1/2 = 50% by default. */
#define LOAD_FACTOR 1, 2

View File

@@ -96,7 +96,7 @@ class Stack {
int64_t size_;
/** The buffer used to implement small object optimization. */
TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
/**
* A chunk referencing the inline buffer. This is always the bottom-most chunk.
@@ -105,7 +105,7 @@ class Stack {
Chunk inline_chunk_;
/** Used for allocations when the inline buffer is not large enough. */
Allocator allocator_;
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
public:
/**

View File

@@ -843,6 +843,18 @@ extern bool BLI_memory_is_zero(const void *arr, size_t arr_size);
*/
#define BLI_ENABLE_IF(condition) typename std::enable_if_t<(condition)> * = nullptr
#if defined(_MSC_VER)
# define BLI_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#elif defined(__has_cpp_attribute)
# if __has_cpp_attribute(no_unique_address)
# define BLI_NO_UNIQUE_ADDRESS [[no_unique_address]]
# else
# define BLI_NO_UNIQUE_ADDRESS
# endif
#else
# define BLI_NO_UNIQUE_ADDRESS [[no_unique_address]]
#endif
/** \} */
#ifdef __cplusplus

View File

@@ -84,10 +84,10 @@ class Vector {
T *capacity_end_;
/** Used for allocations when the inline buffer is too small. */
Allocator allocator_;
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
/** A placeholder buffer that will remain uninitialized until it is used. */
TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
/**
* Store the size of the vector explicitly in debug builds. Otherwise you'd always have to call

View File

@@ -117,10 +117,10 @@ class VectorSet {
uint64_t slot_mask_;
/** This is called to hash incoming keys. */
Hash hash_;
BLI_NO_UNIQUE_ADDRESS Hash hash_;
/** This is called to check equality of two keys. */
IsEqual is_equal_;
BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_;
/** The max load factor is 1/2 = 50% by default. */
#define LOAD_FACTOR 1, 2