Conversation
ukanwat
force-pushed
the
perf/lazy-spec-signature
branch
2 times, most recently
from
September 14, 2026 16:19
0c9fadb to
617504f
Compare
mjs_setName called CheckRepeat, which copies every name of the element's type into a vector, sorts it and scans for neighbours, so naming N elements costs O(N^2 log N). Keep a per-type count of names in use instead, built lazily and updated on each rename. Any structural change (attach, delete, copy, compile) goes through ProcessLists, which invalidates the counts since names can be rewritten there. Behaviour is unchanged: a rename still fails if any repeated name exists within the type, with the same error message, and the compile-time check is untouched. Adds spec_benchmark_test covering flat geoms, flat bodies, a nested body chain and add-then-name geoms. On arm64 macOS naming 8000 geoms drops from 2.7s to 1.0s, the same as adding them unnamed.
Every mjs_add*, delete and attach recomputed the spec signature eagerly, which serializes the whole model tree and hashes it, so building a model of N elements was O(N^2) (google-deepmind#3397). Mark the signature stale on structural changes instead and recompute it on demand: at compile, in CopyBack, and through the new mjs_getSignature(), which the Python .signature property and bind now use. The eager value was already not authoritative between edits, since changing joint or sensor types through the struct does not touch it and Compile recomputes it for that reason, so no existing guarantee is weakened. Regenerates introspect/functions.py, the API docs and the WASM bindings for the new function. Spec benchmark on arm64 macOS: 8000 geoms 748 ms -> 79 ms, 8000 bodies 1784 ms -> 237 ms, 1000 nested bodies 458 ms -> 6 ms.
ukanwat
force-pushed
the
perf/lazy-spec-signature
branch
from
September 21, 2026 00:56
617504f to
1b34227
Compare
copybara-service Bot
pushed a commit
that referenced
this pull request
Sep 21, 2026
GitHub Pull Request: #3576 Procedural model editing was quadratic in the number of elements (#3397): every mjs_add* recomputed the spec signature, which serializes and hashes the whole model tree, and mjs_setName rescanned every name of the element's type to look for duplicates. Behavioural changes: - The signature is now cleared (set to 0) on structural edits and computed once at the end of mj_compile, so mjsElement.signature is 0 whenever the spec topology has been modified since it was last compiled. - Duplicate names are tracked in a per-type std::unordered_set (names_), updated in place on mjs_setName and refreshed in ProcessLists. Failed mjs_setName calls now leave the element's previous name unchanged rather than overwriting it with the duplicate name. Adds spec_benchmark_test. On arm64 macOS: 8000 geoms 748 ms -> 79 ms, 8000 bodies 1784 ms -> 237 ms, 1000 nested bodies 458 ms -> 6 ms, and naming 8000 geoms 2.7 s -> 1.0 s. COPYBARA_INTEGRATE_REVIEW=#3576 from ukanwat:perf/lazy-spec-signature 617504f PiperOrigin-RevId: 985370357 Change-Id: Ie6cd1dd412a651cfd2b6269779bbb7264052c507
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses the two dominant costs from #3397, as two commits.
mjs_setName: the duplicate-name check copied and sorted every name of the type on each call, so naming N elements was O(N^2 log N). It now keeps a per-type count of names in use, built lazily and updated per rename, invalidated wherever names can change outside setName (attach, delete, copy, compile). Behaviour is unchanged, same error, same message.
Signature: it was recomputed eagerly on every add/delete/attach by serialising the whole tree. It is now marked stale and recomputed on demand at compile, in CopyBack, and via a new mjs_getSignature(), which the Python .signature property and bind use. The eager value was already stale whenever a joint or sensor type was changed through the struct, and Compile recomputed it for that reason, so no existing guarantee changes. If you'd rather keep the field always valid, the alternative is a per-body subtree hash (O(depth) per add); happy to switch.
Adds test/benchmark/spec_benchmark_test.cc. On arm64 macOS: 8000 geoms 970 ms -> 79 ms, 8000 bodies 2360 ms -> 237 ms, 1000 nested bodies 650 ms -> 6 ms, 8000 named geoms 2700 ms -> 78 ms. Full ctest, Python suite and ASan clean.
I don't need git attribution if importing directly is easier.