Depsgraph: Use LinearAllocator to allocate Relations
I observed allocation becoming a bottleneck when building the depsgraph with scenes with many simple data-blocks. One of the main culprits was the struct that encodes relations between nodes in the graph. Instead of allocating each `Relation` with a separate allocation call, combine them into a `LinearAllocator`. That is must faster because it allocates in large chunks and just bumps an offset on each allocation. In a test file with 30 thousand cube objects, I observe a 1.18x improvement in depsgraph evaluation time, from 1370 to 1163 ms. The depsgraph isn't completely re-allocated when it's rebuilt, so the allocator added in this PR has to be cleared manually. In the future, it can be used for other structs or potentially strings. Pull Request: https://projects.blender.org/blender/blender/pulls/137303
This commit is contained in:
@@ -89,7 +89,6 @@ void deg_graph_remove_unused_noops(Depsgraph *graph)
|
||||
/* Remove the relations. */
|
||||
for (Relation *relation : relations_to_remove) {
|
||||
relation->unlink();
|
||||
delete relation;
|
||||
}
|
||||
|
||||
DEG_DEBUG_PRINTF((::Depsgraph *)graph,
|
||||
|
||||
@@ -86,7 +86,6 @@ void deg_graph_transitive_reduction(Depsgraph *graph)
|
||||
}
|
||||
for (Relation *rel : relations_to_remove) {
|
||||
rel->unlink();
|
||||
delete rel;
|
||||
}
|
||||
num_removed_relations += relations_to_remove.size();
|
||||
relations_to_remove.clear();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "intern/depsgraph.hh" /* own include */
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
@@ -189,8 +190,14 @@ Relation *Depsgraph::add_new_relation(Node *from, Node *to, const char *descript
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create new relation, and add it to the graph. */
|
||||
rel = new Relation(from, to, description);
|
||||
/* Create new relation, and add it to the graph. The type must be trivially destructable for
|
||||
* `.release()` to be okay. If it weren't, we could store the relations with #destruct_ptr on
|
||||
* either the `inlinks` or `outlinks`. But since so many #Relation structs are allocated, it's
|
||||
* probably better for it be a simple type anyway. */
|
||||
static_assert(std::is_trivially_destructible_v<Relation>);
|
||||
rel = this->build_allocator.construct<Relation>(from, to, description).release();
|
||||
from->outlinks.append(rel);
|
||||
to->inlinks.append(rel);
|
||||
rel->flag |= flags;
|
||||
return rel;
|
||||
}
|
||||
@@ -232,6 +239,9 @@ void Depsgraph::clear_all_nodes()
|
||||
clear_id_nodes();
|
||||
delete time_source;
|
||||
time_source = nullptr;
|
||||
/* Memory used by the build allocator is now unused. Rebuild it from scratch. */
|
||||
std::destroy_at(&this->build_allocator);
|
||||
new (&this->build_allocator) LinearAllocator<>();
|
||||
}
|
||||
|
||||
ID *Depsgraph::get_cow_id(const ID *id_orig) const
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "DNA_ID.h" /* for ID_Type and INDEX_ID_MAX */
|
||||
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_threads.h" /* for SpinLock */
|
||||
|
||||
@@ -80,6 +81,12 @@ struct Depsgraph {
|
||||
|
||||
/* Core Graph Functionality ........... */
|
||||
|
||||
/**
|
||||
* Used to decrease the cost of allocating many small structs when building the graph. This is a
|
||||
* viable strategy because the graph is rebuilt from scratch rather than changed in-place.
|
||||
*/
|
||||
LinearAllocator<> build_allocator;
|
||||
|
||||
/* <ID : IDNode> mapping from ID blocks to nodes representing these
|
||||
* blocks, used for quick lookups. */
|
||||
Map<const ID *, IDNode *> id_hash;
|
||||
|
||||
@@ -12,32 +12,6 @@
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
Relation::Relation(Node *from, Node *to, const char *description)
|
||||
: from(from), to(to), name(description), flag(0)
|
||||
{
|
||||
/* Hook it up to the nodes which use it.
|
||||
*
|
||||
* NOTE: We register relation in the nodes which this link connects to here
|
||||
* in constructor but we don't un-register it in the destructor.
|
||||
*
|
||||
* Reasoning:
|
||||
*
|
||||
* - Destructor is currently used on global graph destruction, so there's no
|
||||
* real need in avoiding dangling pointers, all the memory is to be freed
|
||||
* anyway.
|
||||
*
|
||||
* - Un-registering relation is not a cheap operation, so better to have it
|
||||
* as an explicit call if we need this. */
|
||||
from->outlinks.append(this);
|
||||
to->inlinks.append(this);
|
||||
}
|
||||
|
||||
Relation::~Relation()
|
||||
{
|
||||
/* Sanity check. */
|
||||
BLI_assert(from != nullptr && to != nullptr);
|
||||
}
|
||||
|
||||
void Relation::unlink()
|
||||
{
|
||||
/* Sanity check. */
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
struct Node;
|
||||
@@ -35,8 +33,9 @@ enum RelationFlag {
|
||||
|
||||
/* B depends on A (A -> B) */
|
||||
struct Relation {
|
||||
Relation(Node *from, Node *to, const char *description);
|
||||
~Relation();
|
||||
Relation(Node *from, Node *to, const char *description) : from(from), to(to), name(description)
|
||||
{
|
||||
}
|
||||
|
||||
void unlink();
|
||||
|
||||
@@ -46,9 +45,7 @@ struct Relation {
|
||||
|
||||
/* relationship attributes */
|
||||
const char *name; /* label for debugging */
|
||||
int flag; /* Bitmask of RelationFlag) */
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Relation");
|
||||
int flag = 0; /* Bitmask of RelationFlag) */
|
||||
};
|
||||
|
||||
} // namespace blender::deg
|
||||
|
||||
@@ -285,16 +285,7 @@ Node::Node()
|
||||
name = "";
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
/* Free links. */
|
||||
/* NOTE: We only free incoming links. This is to avoid double-free of links
|
||||
* when we're trying to free same link from both its sides. We don't have
|
||||
* dangling links so this is not a problem from memory leaks point of view. */
|
||||
for (Relation *rel : inlinks) {
|
||||
delete rel;
|
||||
}
|
||||
}
|
||||
Node::~Node() = default;
|
||||
|
||||
std::string Node::identifier() const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user