perf: tree-sparse LᵀDL factorization + O(n·depth) multibody solves#15
perf: tree-sparse LᵀDL factorization + O(n·depth) multibody solves#15haixuanTao wants to merge 1 commit into
Conversation
The multibody mass matrix has branch-induced sparsity: M[i,j] != 0 only when DOFs i and j lie on the same root-to-leaf path. Eliminating leaves-to-root factors M = Lt·D·L with zero fill-in (Featherstone RBDA ch. 6; MuJoCo's mj_factorM): L is nonzero only along ancestor chains. - factorization drops O(n^3) -> O(n·depth^2), every solve (gravity rhs and all constraint-init unit-RHS back-solves) O(n^2) -> O(n·depth); - the per-DOF parent array is built from links_static inside gpu_mb_gravity_and_lu and stored in the former lu_pivots buffer, so no binding or host-side changes are needed anywhere; - factors stay in the dense mass_matrices storage (strict lower = L, diagonal = D, upper = stale symmetric copies no solve reads); - the factor and gravity solve run serially on lane 0 with zero workgroup barriers (the loops are data-dependent but barrier-free, which WGSL uniformity permits) — the dense path needed ~5 barriers per column x MAX_MB_DOFS fixed iterations; - lu_solve_in_place keeps its signature and becomes the sparse LtDL solve, so joint/contact/impulse constraint consumers are unchanged. Partial pivoting is dropped: M is SPD (armature/damping keep the diagonal positive), where unpivoted elimination is stable — the same choice MuJoCo makes. Numerics: on a 12-DOF+free-base biped with contacts, max position drift vs the dense pivoted-LU path is 4e-7 after 10 steps, saturating ~7e-4 over 300 steps (rounding-order drift, not divergence). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2b68c9b to
14af227
Compare
|
Batch-scale follow-up (same hardware, native-CUDA backend, 2048–8192 parallel envs, 12-DOF + free-base biped): this is a net regression of 10–15% steps/s vs the dense-LU path, and the DOF-scaling penalty it targeted is unchanged. The root cause is occupancy, not FLOPs: the sparse factor/solve runs serially on lane 0 while the dense path spread its (larger) work across all 32 lanes; at nv ≈ 18–31 that's more serial steps per warp, and the batched regime is throughput-bound. The single-env win reported above is real but comes from fewer barriers in a latency-bound regime that doesn't transfer. Closing for now. The serial per-env elimination here is exactly the inner loop needed for an env-per-lane layout (one warp = 32 envs, MJX/Isaac-style mapping), which is the follow-up I'd pursue — that's an indexing/layout restructure on top of this math, so I'll reopen or supersede with that version. |
The multibody mass matrix has branch-induced sparsity:
M[i,j] ≠ 0only when DOFsiandjlie on the same root-to-leaf path. Eliminating leaves-to-root factorsM = Lᵀ·D·Lwith zero fill-in (Featherstone, RBDA ch. 6 — the same factorization MuJoCo uses inmj_factorM):Lis nonzero only along ancestor chains.What changes
joint_constraints/contact_constraints/impulse finalize): O(n²) → O(n·depth).links_staticinsidegpu_mb_gravity_and_luand stored in the formerlu_pivotsbuffer — no binding or host-side changes anywhere.mass_matricesstorage (strict lower = L, diagonal = D);lu_solve_in_placekeeps its signature, so all consumers compile unchanged.MAX_MB_DOFSfixed iterations.Validation
On a 12-DOF + free-base biped with ground contacts (WebGPU + native-CUDA backends, RTX 5090): max position drift vs the dense pivoted-LU path is 4e-7 after 10 steps, saturating ~7e-4 over 300 contact-rich steps — rounding-order drift, not divergence. Single-env frame times improve ~4% (WebGPU) / ~3% (CUDA-graph capture); the asymptotic win grows with DOF count and env count since the constraint back-solves dominate at batch scale.
Related perf PRs: #9, #13, #14.
🤖 Generated with Claude Code