Repository navigation
Make family and entity iteration order deterministic - #155
Conversation
Families created after their entities and the Nexus entities iterator took their order from a Dictionary, whose hash seed changes per process. Render and simulation order then differed between otherwise identical runs. Claude-Session: https://claude.ai/code/session_01UaLGUVakHTRSr8q7EMhME5
Family members stay in identifier order only until the first removal, which swaps the last member into the gap. The guarantee is a deterministic order. Claude-Session: https://claude.ai/code/session_01UaLGUVakHTRSr8q7EMhME5
Benchmarks stopped building once FirebladeECS required macOS 14 and Sendable components. Align the manifest with fireblade-engine/math (ordo-one/benchmark 1.38, tools 6.3) and make the fixtures and benchmark list Sendable. Claude-Session: https://claude.ai/code/session_01UaLGUVakHTRSr8q7EMhME5
Both paths now sort entity identifiers for a deterministic order. Claude-Session: https://claude.ai/code/session_01UaLGUVakHTRSr8q7EMhME5
PerformanceMeasured with the benchmark package (
The only measurable cost is the sort when a family is first created; repeated Reproduce from |
Membership churnAdded
As expected from the code: component add/remove and Reproduce: same steps as above with |
A family created after its entities was filled by walking
componentIdsByEntity.keys, andNexus.EntitiesIteratordid the same. Swift seedsDictionaryhashing per process, so the order changed between identical runs. In the Fireblade engine this made lockstep frame captures differ at coplanar faces (fireblade-engine/fireblade#287); withSWIFT_DETERMINISTIC_HASHING=1the captures were pixel-identical.Both paths now sort by identifier, as
Nexus+SnapshotandNexus+Clonealready do. The guarantee is a deterministic order, one that depends only on the sequence of operations, not ascending order:UnorderedSparseSetremoval swaps the last member into the gap. The cost is one sort per family creation and per entities iteration. Not adopted:OrderedDictionary(new dependency, O(n) removal on everydestroy) and per-family ordering options (no consumer needs them).NexusDeterministicOrderTests(256 entities) fail before the change and pass after.make test: 199 tests pass.make lint: no new warnings in touched files.Not changed:
update(familyMembership entityId:)still walksfamilyMembersByTraitsin hash order. That affects only the order ofFamilyMemberAddedevents across families, not member order.https://claude.ai/code/session_01UaLGUVakHTRSr8q7EMhME5