[Bf-blender-cvs] [8aa6ff8ec9a] functions: Reimplement the way many arrays are allocated
Jacques Lucke
noreply at git.blender.org
Mon Aug 12 15:56:58 CEST 2019
Commit: 8aa6ff8ec9ad852f6cc3bfde31bd9a0dae59d37d
Author: Jacques Lucke
Date: Mon Aug 12 15:50:08 2019 +0200
Branches: functions
https://developer.blender.org/rB8aa6ff8ec9ad852f6cc3bfde31bd9a0dae59d37d
Reimplement the way many arrays are allocated
This new implementation is more generic and easier to use.
===================================================================
D source/blender/blenlib/BLI_array_allocator.hpp
A source/blender/blenlib/BLI_temporary_allocator.h
A source/blender/blenlib/BLI_temporary_allocator.hpp
M source/blender/blenlib/CMakeLists.txt
A source/blender/blenlib/intern/BLI_temporary_allocator.cpp
M source/blender/simulations/bparticles/action_interface.hpp
M source/blender/simulations/bparticles/attributes.hpp
M source/blender/simulations/bparticles/events.cpp
M source/blender/simulations/bparticles/integrator.cpp
M source/blender/simulations/bparticles/particle_function.cpp
M source/blender/simulations/bparticles/particle_function.hpp
M source/blender/simulations/bparticles/particle_function_builder.cpp
M source/blender/simulations/bparticles/simulate.cpp
M source/blender/simulations/bparticles/step_description_interfaces.cpp
M source/blender/simulations/bparticles/step_description_interfaces.hpp
M source/blender/windowmanager/intern/wm_init_exit.c
D tests/gtests/blenlib/BLI_array_allocator_test.cc
A tests/gtests/blenlib/BLI_temporary_allocator_test.cc
M tests/gtests/blenlib/CMakeLists.txt
===================================================================
diff --git a/source/blender/blenlib/BLI_array_allocator.hpp b/source/blender/blenlib/BLI_array_allocator.hpp
deleted file mode 100644
index dc19caa4f12..00000000000
--- a/source/blender/blenlib/BLI_array_allocator.hpp
+++ /dev/null
@@ -1,228 +0,0 @@
-#pragma once
-
-/**
- * This allocator should be used, when arrays of the same length are often allocated and
- * deallocated. Knowing that all arrays have the same length makes it possible to just store the
- * size of a single element to identify the buffer length, which is a small number usually.
- */
-
-#include "BLI_stack.hpp"
-#include "BLI_vector_adaptor.hpp"
-
-namespace BLI {
-
-class ArrayAllocator {
- private:
- Vector<void *, 16> m_all_pointers;
- Vector<Stack<void *>, 16> m_pointer_stacks;
- uint m_array_length;
-
- public:
- /**
- * Create a new allocator that will allocate arrays with the given length (the element size may
- * vary).
- */
- ArrayAllocator(uint array_length) : m_array_length(array_length)
- {
- }
-
- ArrayAllocator(ArrayAllocator &other) = delete;
-
- ~ArrayAllocator()
- {
- for (void *ptr : m_all_pointers) {
- MEM_freeN(ptr);
- }
- }
-
- /**
- * Get the number of elements in the arrays allocated by this allocator.
- */
- uint array_size() const
- {
- return m_array_length;
- }
-
- /**
- * Allocate an array buffer in which every element has the given size.
- */
- void *allocate(uint element_size)
- {
- Stack<void *> &stack = this->stack_for_element_size(element_size);
- if (stack.size() > 0) {
- return stack.pop();
- }
- void *ptr = MEM_mallocN_aligned(m_array_length * element_size, 64, __func__);
- m_all_pointers.append(ptr);
- return ptr;
- }
-
- /**
- * Deallocate an array buffer that has been allocated with this allocator before.
- */
- void deallocate(void *ptr, uint element_size)
- {
- Stack<void *> &stack = this->stack_for_element_size(element_size);
- BLI_assert(!stack.contains(ptr));
- stack.push(ptr);
- }
-
- /**
- * Allocate a new array of the given type.
- */
- template<typename T> T *allocate()
- {
- return (T *)this->allocate(sizeof(T));
- }
-
- /**
- * Deallocate an array of the given type. It has to be allocated with this allocator before.
- */
- template<typename T> void deallocate(T *ptr)
- {
- return this->deallocate(ptr, sizeof(T));
- }
-
- /**
- * A wrapper for allocated arrays so that they will be deallocated automatically when they go out
- * of scope.
- */
- template<typename T> class ScopedAllocation {
- private:
- ArrayAllocator &m_allocator;
- void *m_ptr;
- uint m_element_size;
-
- public:
- ScopedAllocation(ArrayAllocator &allocator, T *ptr, uint element_size)
- : m_allocator(allocator), m_ptr(ptr), m_element_size(element_size)
- {
- }
-
- ScopedAllocation(ScopedAllocation &other) = delete;
- ScopedAllocation(ScopedAllocation &&other)
- : m_allocator(other.m_allocator), m_ptr(other.m_ptr), m_element_size(other.m_element_size)
- {
- other.m_ptr = nullptr;
- }
-
- ScopedAllocation &operator=(ScopedAllocation &other) = delete;
- ScopedAllocation &operator=(ScopedAllocation &&other)
- {
- this->~ScopedAllocation();
- new (this) ScopedAllocation(std::move(other));
- return *this;
- }
-
- ~ScopedAllocation()
- {
- if (m_ptr != nullptr) {
- m_allocator.deallocate(m_ptr, m_element_size);
- }
- }
-
- operator T *() const
- {
- return (T *)m_ptr;
- }
-
- ArrayAllocator &allocator()
- {
- return m_allocator;
- }
- };
-
- /**
- * Allocate an array with the given element size. The return value is a wrapper around the
- * pointer, so that it is automatically deallocated.
- */
- ScopedAllocation<void> allocate_scoped(uint element_size)
- {
- return ScopedAllocation<void>(*this, this->allocate(element_size), element_size);
- }
-
- /**
- * Allocate an array of the given type. The return value is a wrapper around the pointer, so that
- * it is automatically deallocated.
- */
- template<typename T> ScopedAllocation<T> allocate_scoped()
- {
- return ScopedAllocation<T>(*this, this->allocate<T>(), sizeof(T));
- }
-
- /**
- * This is a simple vector that has been allocated using an array allocator. The maximum size of
- * the vector is determined by the allocator.
- */
- template<typename T> class VectorAdapter {
- private:
- ScopedAllocation<T> m_ptr;
- VectorAdaptor<T> m_vector;
-
- public:
- VectorAdapter(ArrayAllocator &allocator)
- : m_ptr(allocator.allocate_scoped<T>()), m_vector(m_ptr, allocator.array_size())
- {
- }
-
- ~VectorAdapter() = default;
-
- operator VectorAdaptor<T> &()
- {
- return m_vector;
- }
-
- operator ArrayRef<T>()
- {
- return m_vector;
- }
- };
-
- /**
- * This is a simple fixed size array that has been allocated using an array allocator.
- */
- template<typename T> class Array {
- private:
- ScopedAllocation<T> m_ptr;
- uint m_size;
-
- public:
- Array(ArrayAllocator &allocator) : Array(allocator, allocator.array_size())
- {
- }
-
- Array(ArrayAllocator &allocator, uint size)
- : m_ptr(allocator.allocate_scoped<T>()), m_size(size)
- {
- BLI_assert(size <= allocator.array_size());
- }
-
- operator ArrayRef<T>()
- {
- return ArrayRef<T>(m_ptr, m_size);
- }
-
- T &operator[](uint index)
- {
- return ((T *)m_ptr)[index];
- }
-
- ArrayRef<T> as_array_ref()
- {
- return ArrayRef<T>(m_ptr, m_size);
- }
- };
-
- private:
- Stack<void *> &stack_for_element_size(uint element_size)
- {
- BLI_assert(element_size > 0);
- uint index = element_size - 1;
- if (index >= m_pointer_stacks.size()) {
- m_pointer_stacks.append_n_times({}, index - m_pointer_stacks.size() + 1);
- }
- return m_pointer_stacks[index];
- }
-};
-
-}; // namespace BLI
diff --git a/source/blender/blenlib/BLI_temporary_allocator.h b/source/blender/blenlib/BLI_temporary_allocator.h
new file mode 100644
index 00000000000..56b88d641ff
--- /dev/null
+++ b/source/blender/blenlib/BLI_temporary_allocator.h
@@ -0,0 +1,14 @@
+#ifndef __BLI_TEMPORARY_ALLOCATOR_H__
+#define __BLI_TEMPORARY_ALLOCATOR_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void BLI_temporary_buffers_free_all(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLI_TEMPORARY_ALLOCATOR_H__ */
diff --git a/source/blender/blenlib/BLI_temporary_allocator.hpp b/source/blender/blenlib/BLI_temporary_allocator.hpp
new file mode 100644
index 00000000000..e133a67734f
--- /dev/null
+++ b/source/blender/blenlib/BLI_temporary_allocator.hpp
@@ -0,0 +1,167 @@
+/**
+ * This allocation method should be used when a chunk of memory is only used for a short amount
+ * of time. This makes it possible to cache potentially large buffers for reuse.
+ *
+ * Many cpu-bound algorithms can benefit from being split up into several stages, whereby the
+ * output of one stage is written into an array that is read by the next stage. This improves
+ * debugability as well as profilability. Often a reason this is not done is that the memory
+ * allocation might be expensive. The goal of this allocator is to make this a non-issue, by
+ * reusing the same long buffers over and over again.
+ *
+ * The number of allocated buffers should stay in O(number of threads * max depth of stack trace).
+ * Since these numbers are pretty much constant in Blender, the number of chunks allocated should
+ * not increase over time. For that reason, memory might never be deallocated until Blender exists.
+ */
+
+#pragma once
+
+#include "BLI_utildefines.h"
+#include "BLI_vector_adaptor.hpp"
+
+namespace BLI {
+
+void *allocate_temp_buffer(uint size);
+void free_temp_buffer(void *buffer);
+
+template<typename T> T *allocate_temp_array(uint size)
+{
+ return (T *)allocate_temp_buffer(sizeof(T) * size);
+}
+
+class TemporaryBuffer {
+ private:
+ void *m_ptr;
+ uint m_size = 0;
+
+ public:
+ TemporaryBuffer(uint size) : m_ptr(allocate_temp_buffer(size)), m_size(size)
+ {
+ }
+
+ TemporaryBuffer(const TemporaryBuffer &other) = delete;
+ TemporaryBuffer(TemporaryBuffer &&other) : m_ptr(other.m_ptr)
+ {
+ other.m_ptr = nullptr;
+ }
+
+ ~TemporaryBuffer()
+ {
+ if (m_ptr != nullptr) {
+ free_temp_buffer(m_ptr);
+ }
+ }
+
+ TemporaryBuffer &operator=(TemporaryBuffer &other) = delete;
+ TemporaryBuffer &operator=(TemporaryBuffer &&other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+
+ this->~TemporaryBuffer();
+ new (this) TemporaryBuffer(std::move(other));
+ return *this;
+ }
+
+ uint size() const
+ {
+ return m_size;
+ }
+
+ void *ptr() const
+ {
+ return m_ptr;
+ }
+
+ /**
+ * Take ownership over the pointer.
+ */
+ void *extract_ptr()
+ {
+ void *ptr = m_ptr;
+ m_ptr = nullptr;
+ m_size = 0;
+ return ptr;
+ }
+};
+
+template<typename T> class TemporaryVector {
+ private:
+ TemporaryBuffer m_buffer;
+ VectorAdaptor<T> m_vector;
+
+ public:
+ TemporaryVector(uint capacity)
+ : m_buffer(capacity * sizeof(T)), m_vector((T *)m_buffer.ptr(), capacity)
+ {
+ }
+
+ ~TemporaryVector()
+ {
+ m_vector.clear();
+ }
+
+ operator VectorAdaptor<T> &()
+ {
+ return m_vector;
+ }
+
+ operator ArrayRef<T>()
+ {
+ return m_vector;
+ }
+
+ T &operator[](uint index)
+ {
+ return m_vector[index];
+ }
+
+ VectorAdaptor<T> *operator->()
+ {
+ return &m_vector;
+ }
+};
+
+template<typename T> class TemporaryArray {
+ private:
+ TemporaryBuffer m_buffer;
+ ArrayRef<T> m_array;
+
+ public:
+ TemporaryArray(uint size) : m_buffer(size * sizeof(T)), m_array((T *)m_buffer.ptr(), size)
+ {
+ }
+
+ operator ArrayRef<T>()
+ {
+ return m_array;
+ }
+
+ ArrayRef<T> *operator->()
+ {
+ return &m_array;
+ }
+
+ T &operator[](uint index)
+ {
+ return m_array[index];
+ }
+
+ T *ptr()
+ {
+ return (T *)m_buffer.ptr();
+ }
+
+ /**
+ * Get the array ref and take ownership of the data.
+ */
+ ArrayRef<T> extract()
+ {
+ ArrayRef<T> array_ref = m_array;
+ m_array = {};
+ m_buffer.extract_ptr();
+
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list