BE-607: HashQL: Refactor ModuleRegistry into builder/immutable split with allocator-generic stdlib construction#8887
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
PR SummaryHigh Risk Overview Standard library construction drops the large nested API cleanup: Reviewed by Cursor Bugbot for commit e5e144c. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Pull request overview
Refactors HashQL’s module system and stdlib construction to reduce construction cost and memory pressure by introducing an allocator-generic stdlib module cache and a builder/immutable split for ModuleRegistry.
Changes:
- Reworked stdlib construction around
ModuleCache+CacheId(preorder indexing) and aStandardLibraryContext, replacing the prior nestedSmallVec/TypeIdlookup approach. - Introduced
PartialModuleRegistryas a mutable builder and madeModuleRegistryimmutable post-construction (contiguousIdSlicebacking; root namespace no longer behind anRwLock). - Updated stdlib modules + symbol definitions to use pre-interned
sym::constants /sym::path::…constants, and adjusted resolver/namespace logic to match the new registry backing.
Reviewed changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| libs/@local/hashql/core/src/symbol/sym.rs | Adds new sym:: and sym::path::… constants used by the refactored stdlib modules. |
| libs/@local/hashql/core/src/module/std_lib/mod.rs | Introduces CacheId, ModuleCache, allocator-generic ModuleDef, and the new stdlib build pipeline. |
| libs/@local/hashql/core/src/module/std_lib/kernel/type.rs | Ports kernel type module to StandardLibraryContext + ModuleCache and assigns a CacheId. |
| libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs | Ports kernel special_form module to new context/cache and uses Interned::empty() for 0-generics. |
| libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs | Ports kernel module scaffold to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs | Ports principal stdlib module to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs | Updates module to request dependencies via ModuleCache and allocator-generic ModuleDef. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs | Updates module to request dependencies via ModuleCache and allocator-generic ModuleDef. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs | Ports ontology stdlib module to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs | Adds EntityType/EntityTypeMetadata stdlib types using sym:: constants and cache-based deps. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs | Ports graph types module scaffold to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs | Ports knowledge module scaffold to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs | Ports the large Entity type module to new context/cache and symbol constants. |
| libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs | Ports tmp module to new context/cache and removes empty-generic interning. |
| libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs | Ports temporal module to new context/cache and allocator-generic ModuleDef. |
| libs/@local/hashql/core/src/module/std_lib/graph/tail.rs | Ports tail module to new context/cache and cache-based dependency requests. |
| libs/@local/hashql/core/src/module/std_lib/graph/mod.rs | Ports graph root module; defines Graph<T> type using sym::path::graph::Graph and internal marker symbol. |
| libs/@local/hashql/core/src/module/std_lib/graph/head.rs | Ports head module to new context/cache + cache-based dependency requests. |
| libs/@local/hashql/core/src/module/std_lib/graph/entity.rs | Ports graph entity module to new context/cache + cache-based dependency requests. |
| libs/@local/hashql/core/src/module/std_lib/graph/body.rs | Ports body module to new context/cache + cache-based dependency requests. |
| libs/@local/hashql/core/src/module/std_lib/core/uuid.rs | Ports uuid stdlib module to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/core/url.rs | Ports url stdlib module to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/core/result.rs | Ports result stdlib module to new context/cache + CacheId, uses sym::path::…. |
| libs/@local/hashql/core/src/module/std_lib/core/option.rs | Ports option stdlib module to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/core/mod.rs | Updates core module scaffold and makes func allocator-generic for new ModuleDef. |
| libs/@local/hashql/core/src/module/std_lib/core/math.rs | Ports math intrinsic declarations to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/core/json.rs | Adds JsonPath/JsonPathSegment types using symbol constants + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/core/cmp.rs | Ports cmp intrinsics to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/core/bool.rs | Ports bool intrinsics to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/std_lib/core/bits.rs | Ports bits intrinsics to new context/cache + CacheId. |
| libs/@local/hashql/core/src/module/resolver.rs | Updates resolver to the new ModuleRegistry backing (IdSlice + slice iterators) and adapts tests to builder API. |
| libs/@local/hashql/core/src/module/namespace.rs | Updates namespace import logic for immutable root and adapts tests to builder API and heap slice allocation. |
| libs/@local/hashql/core/src/module/mod.rs | Introduces PartialModuleRegistry, makes ModuleRegistry immutable, swaps traversal visited-set to DenseBitSet, and adds test helpers. |
| libs/@local/hashql/core/src/module/item.rs | Updates Item helpers to index into IdSlice-backed modules and adds #[must_use] annotations. |
| libs/@local/hashql/core/src/lib.rs | Enables maybe_uninit_fill feature needed by the new MaybeUninit-based construction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## bm/be-538-hashql-permission-system-integration #8887 +/- ##
===================================================================================
+ Coverage 23.54% 63.46% +39.92%
===================================================================================
Files 551 899 +348
Lines 27759 78094 +50335
Branches 3066 4628 +1562
===================================================================================
+ Hits 6535 49565 +43030
- Misses 21130 27962 +6832
- Partials 94 567 +473
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
272a5c3 to
e5e144c
Compare
9a2df18 to
12fa94b
Compare
Merging this PR will improve performance by 12.23%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | bit_matrix/dense/iter_row[64] |
140.8 ns | 170 ns | -17.16% |
| ❌ | pattern_match_constant |
150.8 ns | 180 ns | -16.2% |
| ❌ | unique[100] |
25 µs | 29.5 µs | -15.18% |
| ❌ | bit_matrix/dense/iter_row[200] |
185.8 ns | 215 ns | -13.57% |
| ⚡ | runtime |
58.9 ns | 29.7 ns | +98.13% |
| ⚡ | constant |
58.9 ns | 29.7 ns | +98.13% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing bm/be-607-hashql-remove-smallvec-from-the-standardlibrary-for-module (e5e144c) with main (fbfcb98)1
Footnotes
Benchmark results
|
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2002 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1001 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 3314 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 1526 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 2078 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 1033 | Flame Graph |
policy_resolution_medium
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 102 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 51 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 269 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 107 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 133 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 63 | Flame Graph |
policy_resolution_none
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 8 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 3 | Flame Graph |
policy_resolution_small
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 25 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 94 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 26 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 66 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 29 | Flame Graph |
read_scaling_complete
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id;one_depth | 1 entities | Flame Graph | |
| entity_by_id;one_depth | 10 entities | Flame Graph | |
| entity_by_id;one_depth | 25 entities | Flame Graph | |
| entity_by_id;one_depth | 5 entities | Flame Graph | |
| entity_by_id;one_depth | 50 entities | Flame Graph | |
| entity_by_id;two_depth | 1 entities | Flame Graph | |
| entity_by_id;two_depth | 10 entities | Flame Graph | |
| entity_by_id;two_depth | 25 entities | Flame Graph | |
| entity_by_id;two_depth | 5 entities | Flame Graph | |
| entity_by_id;two_depth | 50 entities | Flame Graph | |
| entity_by_id;zero_depth | 1 entities | Flame Graph | |
| entity_by_id;zero_depth | 10 entities | Flame Graph | |
| entity_by_id;zero_depth | 25 entities | Flame Graph | |
| entity_by_id;zero_depth | 5 entities | Flame Graph | |
| entity_by_id;zero_depth | 50 entities | Flame Graph |
read_scaling_linkless
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | 1 entities | Flame Graph | |
| entity_by_id | 10 entities | Flame Graph | |
| entity_by_id | 100 entities | Flame Graph | |
| entity_by_id | 1000 entities | Flame Graph | |
| entity_by_id | 10000 entities | Flame Graph |
representative_read_entity
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1
|
Flame Graph |
representative_read_entity_type
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| get_entity_type_by_id | Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba
|
Flame Graph |
representative_read_multiple_entities
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_property | traversal_paths=0 | 0 | |
| entity_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=0 | 0 | |
| link_by_source_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true |
scenarios
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| full_test | query-limited | Flame Graph | |
| full_test | query-unlimited | Flame Graph | |
| linked_queries | query-limited | Flame Graph | |
| linked_queries | query-unlimited | Flame Graph |

🌟 What is the purpose of this PR?
Replaces the
StandardLibrarytype (12.9KB on the stack due to nestedSmallVec) with a flatModuleCachebacked byMaybeUninitslots and aFiniteBitSet, and restructuresModuleRegistryconstruction around a builder pattern (PartialModuleRegistry). Reduces stdlib construction cost by ~20% in instructions and ~60% in L1D cache misses.🔍 What does this change?
Module cache and construction:
SmallVec<(TypeId, ModuleDef)>inStandardLibrarywith a flatModuleCacheusingBox<[MaybeUninit<ModuleDef>]>andFiniteBitSet<CacheId, u64>for initialization trackingStandardLibraryintoStandardLibrary(coordinator),StandardLibraryContext(passed todefinemethods), andModuleCache(shared across modules)CacheIdenum with one variant per stdlib module in preorder traversal order, replacing runtimeTypeIdlinear scan with compile-time indicesModuleDeftracksemitted_lenfor exact output capacity when building item slicesmatchinbuild()instead of[Option<ItemKind>; 2].flatten()for item emissionbuild()to construct children before the output slice, allowing bump allocator reclamation of intermediate allocationsInternSet) since module item slices are provably unique byModuleIdownershipRegistry restructuring:
PartialModuleRegistryas a mutable builder that collects modules during constructionModuleRegistryis now immutable after construction, backed by a contiguousIdSliceinstead ofInternMapRwLockfrom the root namespace (mutable access during construction, shared reference after)ModuleRegistry::builder()test helper andPartialModuleRegistry::insert_root_module()for tests that inject custom modulesSymbol and type cleanup:
heap.intern_symbol("...")calls in stdlib modules with pre-internedsym::constants.opaque("::...")calls withsym::path::nested constantsInterned::empty()for zero-genericdecl!expansions and special form typesModuleDefparameterized by allocatorSfor bump-scope 2.x supportBenchmark results (Apple Silicon, bump-scope 2.x):
Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
🐾 Next steps
Potential further optimizations identified but not implemented:
TypeIds (number, boolean, etc.) onTypeBuilderto avoid repeated intern round-tripsTypeDefs in modules likemathandspecial_formdecl!macro🛡 What tests cover this?
hashql-coretest suite (880 unit + 326 integration + 20 doc tests) all passmodule::namespaceandmodule::resolvertests exercise custom module construction through the newPartialModuleRegistrybuilder API❓ How to test this?
cargo test --package hashql-core