mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 23:11:00 +00:00
ifcparse: unregister a deleted instance's inverse records via its attributes (#9467)
process_deletion_inverse() called inverse_index::remove_source(), which
walked every record in the file's inverse index to find the ones whose
source is the deleted instance: O(R) per deletion, the dominant cost of
file.remove() on large files now that the lookup side no longer re-sorts.
The records a deleted instance contributed are exactly the entity
references in its own attributes, so walk those with the same visitor
build_inverses_() uses for registration and remove each record with a
targeted binary search instead. remove_source() has no callers left and
is deleted.
Also use the ordered view of batch_deletion_ids_ (a boost multi_index
that already had one) for the is-this-referencer-also-being-deleted
check in process_deletion_(), which was a linear std::find over the
sequenced view: O(b) per referencing instance made batch deletion of b
instances quadratic.
file.remove on 300 IfcPropertySet of a 155 MB IFC4 model (201k IfcRoot)
drops from 3.15 ms to 0.17 ms per call, batched removal of 2000 from
3.34 ms to 0.17 ms per call, root.remove_product on 100 walls from
332 ms to 131 ms per call.
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH
(cherry picked from commit 938442303f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+15
-2
@@ -2940,7 +2940,7 @@ void file::process_deletion_(const express::base& entity) {
|
||||
// entity being deleted are not deleted themselves.
|
||||
if (!references.empty()) {
|
||||
for (auto& related_instance : references) {
|
||||
if (std::find(batch_deletion_ids_.begin(), batch_deletion_ids_.end(), related_instance.id()) != batch_deletion_ids_.end()) {
|
||||
if (batch_deletion_ids_.get<1>().count((int)related_instance.id()) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3021,7 +3021,20 @@ void ifcopenshell::impl::in_memory_file_storage::process_deletion_inverse(const
|
||||
|
||||
// Delete inverses into entity
|
||||
byref_excl_.erase(id);
|
||||
byref_excl_.remove_source(id);
|
||||
|
||||
// Delete the records the entity contributed through its own attributes.
|
||||
// Walking the attributes mirrors build_inverses_, so every record with
|
||||
// this source is covered without scanning the whole index for it.
|
||||
const auto* decl = entity.declaration().as_entity();
|
||||
if (decl == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::function<void(const express::base&, int)> fn = [this, id, decl](const express::base& attr, int idx) {
|
||||
if (attr.declaration().as_entity() != nullptr) {
|
||||
byref_excl_.remove(attr.id(), id, (uint16_t)decl->index_in_schema(), idx);
|
||||
}
|
||||
};
|
||||
apply_individual_instance_visitor(entity).apply(fn);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Reference in New Issue
Block a user