From e713fdb17e3e5f3a8348d342ad13b1bb901e0517 Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Tue, 1 Sep 2026 12:41:34 +0300 Subject: [PATCH] geom: cache hybrid kernel component resolution (#9371) Constructing a hybrid kernel (e.g. hybrid-cgal-simple-opencascade) ran find_kernel_match once per component on every construction. Each call creates a plugin manager, walks the plugin search paths and loads every geometry_kernel_* module from disk before matching the component name. Named kernels only pay this once because they are registered in the static registry after the first load, but a hybrid name is never registered, so every iterator or create_shape construction repeated the full scan (measured 28-100 ms per component on the reference model, ~70 ms extra per create_shape call; far worse on platforms where library loading is expensive). Cache the resolved component backend ids per hybrid name. The first construction still performs the full discovery and registers the matched modules; subsequent constructions build the components straight from the registry without touching the disk. --- src/ifcgeom/kernel_registry.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/ifcgeom/kernel_registry.cpp b/src/ifcgeom/kernel_registry.cpp index d2657ca8e7..840f73f638 100644 --- a/src/ifcgeom/kernel_registry.cpp +++ b/src/ifcgeom/kernel_registry.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include namespace ifcopenshell { @@ -110,6 +112,11 @@ namespace { return match; } + + // Component resolution scans the plugin directory and loads modules from + // disk, which is too expensive to repeat on every kernel construction. + std::mutex hybrid_component_mutex; + std::map> hybrid_component_cache; } void ifcopenshell::geom::kernels::kernel_registry::bind(const kernel_info& info, create_fn create, const plugin::module& module) { @@ -149,6 +156,22 @@ std::unique_ptr ifcopenshell::geom auto geometry_library_lower = boost::to_lower_copy(geometry_library); auto& registry = kernel_registry_instance(); + if (geometry_library_lower.rfind("hybrid-", 0) == 0) { + std::lock_guard lock(hybrid_component_mutex); + auto cached = hybrid_component_cache.find(geometry_library_lower); + if (cached != hybrid_component_cache.end()) { + std::vector> kernels; + for (const auto& backend_id : cached->second) { + kernels.push_back(registry.create(backend_id, file, settings, logger)); + } + for (auto it = kernels.begin(); it != kernels.end(); ++it) { + (**it).propagate_exceptions = it == kernels.begin(); + (**it).partial_success_is_success = it == kernels.end() - 1; + } + return std::make_unique(geometry_library, file, settings, std::move(kernels), logger); + } + } + if (!registry.has(geometry_library_lower)) { load_kernel_plugin(registry, geometry_library_lower); } @@ -157,8 +180,13 @@ std::unique_ptr ifcopenshell::geom } if (geometry_library_lower.rfind("hybrid-", 0) == 0) { + const auto hybrid_key = geometry_library_lower; + + std::lock_guard lock(hybrid_component_mutex); + geometry_library_lower = geometry_library_lower.substr(strlen("hybrid")); std::vector> kernels; + std::vector component_backend_ids; while (!geometry_library_lower.empty()) { if (geometry_library_lower.find("-", 0) == 0) { geometry_library_lower = geometry_library_lower.substr(strlen("-")); @@ -174,6 +202,7 @@ std::unique_ptr ifcopenshell::geom } kernels.push_back(registry.create(matched_backend_id, file, settings, logger)); + component_backend_ids.push_back(matched_backend_id); geometry_library_lower = geometry_library_lower.substr(matched_backend_id.size()); } @@ -183,6 +212,7 @@ std::unique_ptr ifcopenshell::geom } if (!kernels.empty()) { + hybrid_component_cache.emplace(hybrid_key, std::move(component_backend_ids)); return std::make_unique(geometry_library, file, settings, std::move(kernels), logger); } }