-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Using CellKey/VertexKey internally will almost certainly be faster and leaner than using Uuid everywhere. Keep UUIDs for I/O (serde, logs, external references), but prefer keys for all hot paths.
Here’s why and what to change:
Why keys beat UUIDs
• Less hashing & indirection. A Uuid (16 bytes) must be hashed/compared each time you touch a map or set. slotmap keys are small, copyable integers with cheap hashing/comparison.
• Fewer bimap lookups. Your code often does Uuid ⇄ Key round-trips (e.g., neighbor/incident assignment). If cells/vertices store keys instead of UUIDs, you skip at least one map lookup per step.
• Better cache locality & memory. Storing CellKey / VertexKey (and even Option) shrinks neighbor arrays, facet maps, and incident pointers vs Uuids.
• Sorting/sets are cheaper. Places like remove_duplicate_cells() build sorted vertex IDs; sorting keys is faster than sorting UUIDs.
What to switch (safely)
• Inside Cell: store neighbors: Option<Vec<Option>> (or fixed-size Option<[Option; D+1]>), not Uuid.
• Inside Vertex: store incident_cell: Option, not Uuid.
• Working maps: build everything with keys:
• FacetToCellsMap keyed by your facet hash computed from vertex keys (you’re already partway there).
• VertexToCellsMap, CellNeighborsMap, etc., should hold keys.
• Only convert at the boundary:
• When serializing or public API methods need stable IDs, translate keys → UUID by reading the object (cells/vertices already store their own UUID field).
• Keep a one-way HashMap<Uuid, VertexKey> / HashMap<Uuid, CellKey> for incoming UUID lookups. You likely don’t need the full BiMap once internals use keys, because key→uuid can be read from the element itself.
Gotchas to mind
• Serde stability: slotmap keys aren’t stable across runs; keep UUIDs as your serialized identity and reconstruct maps on load.
• APIs/tests: Anywhere that currently returns UUID neighbors/incident cells will need a small conversion helper (key → uuid).
• Bench it: Use criterion to compare:
• assign_neighbors()
• assign_incident_cells()
• remove_duplicate_cells() (with key-sorting vs uuid-sorting)
• Incremental insertion (per-vertex)
A minimal migration path
1. Add parallel fields:
• neighbors_keys: Option<Vec<Option>>
• incident_cell_key: Option
2. Update construction/assignment code to fill the key fields first.
3. (Optionally) keep the UUID fields for compatibility, populating them from the key fields only when needed (or behind feature flags).
4. After you flip callers to use keys, remove the UUID internals.
Expected impact (rule-of-thumb)
You’ll typically see lower constant factors in hot loops (neighbor assignment, cavity building, duplicate checks) and less memory churn. It’s not a new algorithmic win, but in geom/data-structure code like this, that’s often a noticeable improvement.