BLI: add typedefs for containers that use raw allocators

Those are useful when you have to create containers with static
storage duration. If those would use Blender's guarded allocator,
it would report memory leaks, that are not actually leaks.
This commit is contained in:
2020-07-20 16:00:20 +02:00
parent ed184050b6
commit ccc2a7996b
10 changed files with 81 additions and 27 deletions

View File

@@ -53,11 +53,8 @@ template<
typename T,
/**
* The number of values that can be stored in the array, without doing a heap allocation.
*
* When T is large, the small buffer optimization is disabled by default to avoid large
* unexpected allocations on the stack. It can still be enabled explicitly though.
*/
int64_t InlineBufferCapacity = (sizeof(T) < 100) ? 4 : 0,
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
/**
* The allocator used by this array. Should rarely be changed, except when you don't want that
* MEM_* functions are used internally.
@@ -364,6 +361,13 @@ class Array {
}
};
/**
* Same as a normal Array, but does not use Blender's guarded allocator. This is useful when
* allocating memory with static storage duration.
*/
template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
using RawArray = Array<T, InlineBufferCapacity, RawAllocator>;
} // namespace blender
#endif /* __BLI_ARRAY_HH__ */

View File

@@ -92,11 +92,8 @@ template<
* The minimum number of elements that can be stored in this Map without doing a heap
* allocation. This is useful when you expect to have many small maps. However, keep in mind
* that (unlike vector) initializing a map has a O(n) cost in the number of slots.
*
* When Key or Value are large, the small buffer optimization is disabled by default to avoid
* large unexpected allocations on the stack. It can still be enabled explicitly though.
*/
int64_t InlineBufferCapacity = (sizeof(Key) + sizeof(Value) < 100) ? 4 : 0,
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key) + sizeof(Value)),
/**
* The strategy used to deal with collisions. They are defined in BLI_probing_strategies.hh.
*/
@@ -1162,6 +1159,21 @@ class Map {
}
};
/**
* Same as a normal Map, but does not use Blender's guarded allocator. This is useful when
* allocating memory with static storage duration.
*/
template<typename Key,
typename Value,
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key) +
sizeof(Value)),
typename ProbingStrategy = DefaultProbingStrategy,
typename Hash = DefaultHash<Key>,
typename IsEqual = DefaultEquality,
typename Slot = typename DefaultMapSlot<Key, Value>::type>
using RawMap =
Map<Key, Value, InlineBufferCapacity, ProbingStrategy, Hash, IsEqual, Slot, RawAllocator>;
/**
* A wrapper for std::unordered_map with the API of blender::Map. This can be used for
* benchmarking.

View File

@@ -419,6 +419,15 @@ template<typename From, typename To>
inline constexpr bool is_convertible_pointer_v =
std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>;
/**
* Inline buffers for small-object-optimization should be disable by default. Otherwise we might
* get large unexpected allocations on the stack.
*/
inline constexpr int64_t default_inline_buffer_capacity(size_t element_size)
{
return ((int64_t)element_size < 100) ? 4 : 0;
}
} // namespace blender
#endif /* __BLI_MEMORY_UTILS_HH__ */

View File

@@ -89,11 +89,8 @@ template<
* The minimum number of elements that can be stored in this Set without doing a heap
* allocation. This is useful when you expect to have many small sets. However, keep in mind
* that (unlike vector) initializing a set has a O(n) cost in the number of slots.
*
* When Key is large, the small buffer optimization is disabled by default to avoid large
* unexpected allocations on the stack. It can still be enabled explicitly though.
*/
int64_t InlineBufferCapacity = (sizeof(Key) < 100) ? 4 : 0,
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key)),
/**
* The strategy used to deal with collisions. They are defined in BLI_probing_strategies.hh.
*/
@@ -821,6 +818,18 @@ template<typename Key> class StdUnorderedSetWrapper {
}
};
/**
* Same as a normal Set, but does not use Blender's guarded allocator. This is useful when
* allocating memory with static storage duration.
*/
template<typename Key,
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key)),
typename ProbingStrategy = DefaultProbingStrategy,
typename Hash = DefaultHash<Key>,
typename IsEqual = DefaultEquality,
typename Slot = typename DefaultSetSlot<Key>::type>
using RawSet = Set<Key, InlineBufferCapacity, ProbingStrategy, Hash, IsEqual, Slot, RawAllocator>;
} // namespace blender
#endif /* __BLI_SET_HH__ */

View File

@@ -73,11 +73,8 @@ template<
* The number of values that can be stored in this stack, without doing a heap allocation.
* Sometimes it can make sense to increase this value a lot. The memory in the inline buffer is
* not initialized when it is not needed.
*
* When T is large, the small buffer optimization is disabled by default to avoid large
* unexpected allocations on the stack. It can still be enabled explicitly though.
*/
int64_t InlineBufferCapacity = (sizeof(T) < 100) ? 4 : 0,
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
/**
* The allocator used by this stack. Should rarely be changed, except when you don't want that
* MEM_* is used internally.
@@ -381,6 +378,13 @@ class Stack {
}
};
/**
* Same as a normal Stack, but does not use Blender's guarded allocator. This is useful when
* allocating memory with static storage duration.
*/
template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
using RawStack = Stack<T, InlineBufferCapacity, RawAllocator>;
} /* namespace blender */
#endif /* __BLI_STACK_HH__ */

View File

@@ -70,7 +70,7 @@ template<
* When T is large, the small buffer optimization is disabled by default to avoid large
* unexpected allocations on the stack. It can still be enabled explicitly though.
*/
int64_t InlineBufferCapacity = (sizeof(T) < 100) ? 4 : 0,
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
/**
* The allocator used by this vector. Should rarely be changed, except when you don't want that
* MEM_* is used internally.
@@ -824,6 +824,13 @@ class Vector {
#undef UPDATE_VECTOR_SIZE
/**
* Same as a normal Vector, but does not use Blender's guarded allocator. This is useful when
* allocating memory with static storage duration.
*/
template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
using RawVector = Vector<T, InlineBufferCapacity, RawAllocator>;
} /* namespace blender */
#endif /* __BLI_VECTOR_HH__ */

View File

@@ -732,6 +732,17 @@ class VectorSet {
}
};
/**
* Same as a normal VectorSet, but does not use Blender's guarded allocator. This is useful when
* allocating memory with static storage duration.
*/
template<typename Key,
typename ProbingStrategy = DefaultProbingStrategy,
typename Hash = DefaultHash<Key>,
typename IsEqual = DefaultEquality,
typename Slot = typename DefaultVectorSetSlot<Key>::type>
using RawVectorSet = VectorSet<Key, ProbingStrategy, Hash, IsEqual, Slot, RawAllocator>;
} // namespace blender
#endif /* __BLI_VECTOR_SET_HH__ */

View File

@@ -24,7 +24,7 @@
namespace blender {
static Vector<Array<int64_t, 0, RawAllocator>, 1, RawAllocator> arrays;
static RawVector<RawArray<int64_t, 0>> arrays;
static int64_t current_array_size = 0;
static int64_t *current_array = nullptr;
static std::mutex current_array_mutex;
@@ -44,7 +44,7 @@ Span<int64_t> IndexRange::as_span() const
}
int64_t new_size = std::max<int64_t>(1000, power_of_2_max_u(min_required_size));
Array<int64_t, 0, RawAllocator> new_array(new_size);
RawArray<int64_t, 0> new_array(new_size);
for (int64_t i = 0; i < new_size; i++) {
new_array[i] = i;
}

View File

@@ -30,9 +30,7 @@
namespace blender {
namespace deg {
/* TODO: Static variables should use RawAllocator to avoid false positives of Blender's memory leak
* detector. */
static Map<Main *, VectorSet<Depsgraph *>> g_graph_registry;
static RawMap<Main *, RawVectorSet<Depsgraph *>> g_graph_registry;
void register_graph(Depsgraph *depsgraph)
{
@@ -43,7 +41,7 @@ void register_graph(Depsgraph *depsgraph)
void unregister_graph(Depsgraph *depsgraph)
{
Main *bmain = depsgraph->bmain;
VectorSet<Depsgraph *> &graphs = g_graph_registry.lookup(bmain);
RawVectorSet<Depsgraph *> &graphs = g_graph_registry.lookup(bmain);
graphs.remove(depsgraph);
// If this was the last depsgraph associated with the main, remove the main entry as well.
@@ -54,7 +52,7 @@ void unregister_graph(Depsgraph *depsgraph)
Span<Depsgraph *> get_all_registered_graphs(Main *bmain)
{
VectorSet<Depsgraph *> *graphs = g_graph_registry.lookup_ptr(bmain);
RawVectorSet<Depsgraph *> *graphs = g_graph_registry.lookup_ptr(bmain);
if (graphs != nullptr) {
return *graphs;
}

View File

@@ -33,9 +33,9 @@ namespace blender::fn {
struct MFSignature {
std::string function_name;
/* Use RawAllocator so that a MultiFunction can have static storage duration. */
Vector<std::string, 4, RawAllocator> param_names;
Vector<MFParamType, 4, RawAllocator> param_types;
Vector<int, 4, RawAllocator> param_data_indices;
RawVector<std::string> param_names;
RawVector<MFParamType> param_types;
RawVector<int> param_data_indices;
int data_index(int param_index) const
{