Optimize mj_flex performance.

Two performance optimizations for interpolated flex objects:

1. Hoist loop-invariant stride calculations in `mju_cellLookup` out of nested loops.
   This reduces multiplications from 16 to 6 (linear) and 54 to 12 (quadratic)
   per vertex.

2. Extract and consolidate optimized 3D interpolation logic into a new
   reusable utility `mju_evalBasisArray` in `engine_util_misc.c`. This function
   uses nested loops and precomputed 1D shape functions to avoid expensive
   dynamic `phi` calls and branching, and leverages stack-buffered outputs to
   eliminate compiler pointer-aliasing barriers. We propagate this optimization
   to both kinematics (`mju_interpolate3D`) and constraint setup
   (`engine_core_constraint.c`).

Together these changes yield a ~50% overall speedup in `mj_fwdKinematics` for interpolated flexes with ~10k vertices in the collision meshes and ~10 nodes in the deformation grid.

PiperOrigin-RevId: 922198093
Change-Id: I9c804e65cd532e2f9ac6e9a71d01c30cd64186fa
This commit is contained in:
Alessio Quaglino
2026-05-27 10:04:37 -07:00
committed by Copybara-Service
parent af4be63cd8
commit f6c85287bf
3 changed files with 88 additions and 11 deletions
+20 -6
View File
@@ -290,13 +290,27 @@ static int mj_vertBodyWeight(const mjModel* m, const mjData* d, int f, int* v,
int nstart = m->flex_nodeadr[f];
int nb = 0;
for (int j = 0; j < npc; j++) {
mjtNum w = mju_evalBasis(local, j, order);
if (w < 1e-5) {
continue;
if (npc > 27) {
for (int j = 0; j < npc; j++) {
mjtNum w = mju_evalBasis(local, j, order);
if (w < 1e-5) {
continue;
}
if (bweight) bweight[nb] = sign * w;
body[nb++] = m->flex_nodebodyid[nstart + nodeindices[j]];
}
} else {
mjtNum basis[27];
mju_evalBasisArray(basis, local, order);
for (int j = 0; j < npc; j++) {
mjtNum w = basis[j];
if (w < 1e-5) {
continue;
}
if (bweight) bweight[nb] = sign * w;
body[nb++] = m->flex_nodebodyid[nstart + nodeindices[j]];
}
if (bweight) bweight[nb] = sign * w;
body[nb++] = m->flex_nodebodyid[nstart + nodeindices[j]];
}
return nb;