Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/functions/intern/multi_function_procedure_executor.cc')
-rw-r--r--source/blender/functions/intern/multi_function_procedure_executor.cc124
1 files changed, 72 insertions, 52 deletions
diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc
index ab2fd7c098c..c6f4212caa6 100644
--- a/source/blender/functions/intern/multi_function_procedure_executor.cc
+++ b/source/blender/functions/intern/multi_function_procedure_executor.cc
@@ -139,32 +139,36 @@ class VariableState;
*/
class ValueAllocator : NonCopyable, NonMovable {
private:
- /* Allocate with 64 byte alignment for better reusability of buffers and improved cache
- * performance. */
+ /**
+ * Allocate with 64 byte alignment for better reusability of buffers and improved cache
+ * performance.
+ */
static constexpr inline int min_alignment = 64;
- /* Use stacks so that the most recently used buffers are reused first. This improves cache
- * efficiency. */
- std::array<Stack<VariableValue *>, tot_variable_value_types> values_free_lists_;
- /* The integer key is the size of one element (e.g. 4 for an integer buffer). All buffers are
- * aligned to #min_alignment bytes. */
+ /** All buffers in the free-lists below have been allocated with this allocator. */
+ LinearAllocator<> &linear_allocator_;
+
+ /**
+ * Use stacks so that the most recently used buffers are reused first. This improves cache
+ * efficiency.
+ */
+ std::array<Stack<VariableValue *>, tot_variable_value_types> variable_value_free_lists_;
+
+ /**
+ * The integer key is the size of one element (e.g. 4 for an integer buffer). All buffers are
+ * aligned to #min_alignment bytes.
+ */
Map<int, Stack<void *>> span_buffers_free_list_;
- public:
- ValueAllocator() = default;
+ /** Cache buffers for single values of different types. */
+ Map<const CPPType *, Stack<void *>> single_value_free_lists_;
- ~ValueAllocator()
+ /** The cached memory buffers can hold #VariableState values. */
+ Stack<void *> variable_state_free_list_;
+
+ public:
+ ValueAllocator(LinearAllocator<> &linear_allocator) : linear_allocator_(linear_allocator)
{
- for (Stack<VariableValue *> &stack : values_free_lists_) {
- while (!stack.is_empty()) {
- MEM_freeN(stack.pop());
- }
- }
- for (Stack<void *> &stack : span_buffers_free_list_.values()) {
- while (!stack.is_empty()) {
- MEM_freeN(stack.pop());
- }
- }
}
template<typename... Args> VariableState *obtain_variable_state(Args &&...args);
@@ -190,17 +194,17 @@ class ValueAllocator : NonCopyable, NonMovable {
{
void *buffer = nullptr;
- const int element_size = type.size();
- const int alignment = type.alignment();
+ const int64_t element_size = type.size();
+ const int64_t alignment = type.alignment();
if (alignment > min_alignment) {
/* In this rare case we fallback to not reusing existing buffers. */
- buffer = MEM_mallocN_aligned(element_size * size, alignment, __func__);
+ buffer = linear_allocator_.allocate(element_size * size, alignment);
}
else {
Stack<void *> *stack = span_buffers_free_list_.lookup_ptr(element_size);
if (stack == nullptr || stack->is_empty()) {
- buffer = MEM_mallocN_aligned(element_size * size, min_alignment, __func__);
+ buffer = linear_allocator_.allocate(element_size * size, min_alignment);
}
else {
/* Reuse existing buffer. */
@@ -224,7 +228,14 @@ class ValueAllocator : NonCopyable, NonMovable {
VariableValue_OneSingle *obtain_OneSingle(const CPPType &type)
{
- void *buffer = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
+ Stack<void *> &stack = single_value_free_lists_.lookup_or_add_default(&type);
+ void *buffer;
+ if (stack.is_empty()) {
+ buffer = linear_allocator_.allocate(type.size(), type.alignment());
+ }
+ else {
+ buffer = stack.pop();
+ }
return this->obtain<VariableValue_OneSingle>(buffer);
}
@@ -262,11 +273,11 @@ class ValueAllocator : NonCopyable, NonMovable {
}
case ValueType::OneSingle: {
auto *value_typed = static_cast<VariableValue_OneSingle *>(value);
+ const CPPType &type = data_type.single_type();
if (value_typed->is_initialized) {
- const CPPType &type = data_type.single_type();
type.destruct(value_typed->data);
}
- MEM_freeN(value_typed->data);
+ single_value_free_lists_.lookup_or_add_default(&type).push(value_typed->data);
break;
}
case ValueType::OneVector: {
@@ -276,7 +287,7 @@ class ValueAllocator : NonCopyable, NonMovable {
}
}
- Stack<VariableValue *> &stack = values_free_lists_[(int)value->type];
+ Stack<VariableValue *> &stack = variable_value_free_lists_[(int)value->type];
stack.push(value);
}
@@ -284,9 +295,9 @@ class ValueAllocator : NonCopyable, NonMovable {
template<typename T, typename... Args> T *obtain(Args &&...args)
{
static_assert(std::is_base_of_v<VariableValue, T>);
- Stack<VariableValue *> &stack = values_free_lists_[(int)T::static_type];
+ Stack<VariableValue *> &stack = variable_value_free_lists_[(int)T::static_type];
if (stack.is_empty()) {
- void *buffer = MEM_mallocN(sizeof(T), __func__);
+ void *buffer = linear_allocator_.allocate(sizeof(T), alignof(T));
return new (buffer) T(std::forward<Args>(args)...);
}
return new (stack.pop()) T(std::forward<Args>(args)...);
@@ -669,7 +680,12 @@ class VariableState : NonCopyable, NonMovable {
tot_initialized_ += mask.size();
}
- void destruct(IndexMask mask,
+ /**
+ * Destruct the masked elements in this variable.
+ * \return True when all elements of this variable are unitialized and the variable state can be
+ * released.
+ */
+ bool destruct(IndexMask mask,
IndexMask full_mask,
const MFDataType &data_type,
ValueAllocator &value_allocator)
@@ -679,13 +695,12 @@ class VariableState : NonCopyable, NonMovable {
/* Sanity check to make sure that enough indices can be destructed. */
BLI_assert(new_tot_initialized >= 0);
+ bool do_destruct_self = false;
+
switch (value_->type) {
case ValueType::GVArray: {
if (mask.size() == full_mask.size()) {
- /* All elements are destructed. The elements are owned by the caller, so we don't
- * actually destruct them. */
- value_allocator.release_value(value_, data_type);
- value_ = value_allocator.obtain_OneSingle(data_type.single_type());
+ do_destruct_self = true;
}
else {
/* Not all elements are destructed. Since we can't work on the original array, we have to
@@ -701,18 +716,13 @@ class VariableState : NonCopyable, NonMovable {
const CPPType &type = data_type.single_type();
type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask);
if (new_tot_initialized == 0) {
- /* Release span when all values are initialized. */
- value_allocator.release_value(value_, data_type);
- value_ = value_allocator.obtain_OneSingle(data_type.single_type());
+ do_destruct_self = true;
}
break;
}
case ValueType::GVVectorArray: {
if (mask.size() == full_mask.size()) {
- /* All elements are cleared. The elements are owned by the caller, so don't actually
- * destruct them. */
- value_allocator.release_value(value_, data_type);
- value_ = value_allocator.obtain_OneVector(data_type.vector_base_type());
+ do_destruct_self = true;
}
else {
/* Not all elements are cleared. Since we can't work on the original vector array, we
@@ -731,23 +741,24 @@ class VariableState : NonCopyable, NonMovable {
case ValueType::OneSingle: {
auto *value_typed = this->value_as<VariableValue_OneSingle>();
BLI_assert(value_typed->is_initialized);
+ UNUSED_VARS_NDEBUG(value_typed);
if (mask.size() == tot_initialized_) {
- const CPPType &type = data_type.single_type();
- type.destruct(value_typed->data);
- value_typed->is_initialized = false;
+ do_destruct_self = true;
}
break;
}
case ValueType::OneVector: {
auto *value_typed = this->value_as<VariableValue_OneVector>();
+ UNUSED_VARS(value_typed);
if (mask.size() == tot_initialized_) {
- value_typed->data.clear({0});
+ do_destruct_self = true;
}
break;
}
}
tot_initialized_ = new_tot_initialized;
+ return do_destruct_self;
}
void indices_split(IndexMask mask, IndicesSplitVectors &r_indices)
@@ -801,12 +812,17 @@ class VariableState : NonCopyable, NonMovable {
template<typename... Args> VariableState *ValueAllocator::obtain_variable_state(Args &&...args)
{
- return new VariableState(std::forward<Args>(args)...);
+ if (variable_state_free_list_.is_empty()) {
+ void *buffer = linear_allocator_.allocate(sizeof(VariableState), alignof(VariableState));
+ return new (buffer) VariableState(std::forward<Args>(args)...);
+ }
+ return new (variable_state_free_list_.pop()) VariableState(std::forward<Args>(args)...);
}
void ValueAllocator::release_variable_state(VariableState *state)
{
- delete state;
+ state->~VariableState();
+ variable_state_free_list_.push(state);
}
/** Keeps track of the states of all variables during evaluation. */
@@ -817,7 +833,8 @@ class VariableStates {
IndexMask full_mask_;
public:
- VariableStates(IndexMask full_mask) : full_mask_(full_mask)
+ VariableStates(LinearAllocator<> &linear_allocator, IndexMask full_mask)
+ : value_allocator_(linear_allocator), full_mask_(full_mask)
{
}
@@ -939,7 +956,10 @@ class VariableStates {
void destruct(const MFVariable &variable, const IndexMask &mask)
{
VariableState &variable_state = this->get_variable_state(variable);
- variable_state.destruct(mask, full_mask_, variable.data_type(), value_allocator_);
+ if (variable_state.destruct(mask, full_mask_, variable.data_type(), value_allocator_)) {
+ variable_state.destruct_self(value_allocator_, variable.data_type());
+ variable_states_.remove_contained(&variable);
+ }
}
VariableState &get_variable_state(const MFVariable &variable)
@@ -1161,9 +1181,9 @@ void MFProcedureExecutor::call(IndexMask full_mask, MFParams params, MFContext c
{
BLI_assert(procedure_.validate());
- LinearAllocator<> allocator;
+ LinearAllocator<> linear_allocator;
- VariableStates variable_states{full_mask};
+ VariableStates variable_states{linear_allocator, full_mask};
variable_states.add_initial_variable_states(*this, procedure_, params);
InstructionScheduler scheduler;