Rejigged solver statistics in preparation for islanding.

- See public description below.
- Stopped incrementing the iteration count in saveStats().
- `sizeof(mjSolverStat) == 40`, so this ends up costing 160KB, up from 40KB.

BEGIN_PUBLIC

Changed the size of `mjData.solver`, the structure used to collect solver diagnostic information. The array is now of length `mjNISLAND * mjNSOLVER`, where each row of length `mjNSOLVER` contains separate solver statistics for each constraint island. Until solver islanding is implemented, only row 0 is used.

- The new constant `mjNISLAND` is set to 20.
- `mjNSOLVER` is reduced from 1000 to 200.
- Added `mjData.solver_nisland`, the number of islands for which the solver ran.
- `mjData.solver_niter` (renamed from mjData.solver_iter) and `mjData.solver_nnz` are now integer vectors of length `mjNISLAND`.

END_PUBLIC

PiperOrigin-RevId: 565030093
Change-Id: I773e918805c6ced79f0dab5f19ea23956760c8c5
This commit is contained in:
Yuval Tassa
2023-09-13 06:17:45 -07:00
committed by Copybara-Service
parent 8faf47dd16
commit 86d8b912f4
15 changed files with 3303 additions and 155 deletions
+115 -53
View File
@@ -176,6 +176,10 @@ const char help_title[] =
//-------------------------------- profiler, sensor, info, watch -----------------------------------
// number of lines in the Constraint ("Counts") and Cost ("Convergence") figures
static constexpr int kConstraintNum = 5;
static constexpr int kCostNum = 3;
// init profiler figures
void InitializeProfiler(mj::Simulate* sim) {
// set figures to default
@@ -212,6 +216,20 @@ void InitializeProfiler(mj::Simulate* sim) {
sim->figsize.figurergba[3] = 0.5f;
sim->figtimer.figurergba[3] = 0.5f;
// repeat line colors for constraint and cost figures
mjvFigure* fig = &sim->figcost;
for (int i=kCostNum; i<mjMAXLINE; i++) {
fig->linergb[i][0] = fig->linergb[i - kCostNum][0];
fig->linergb[i][1] = fig->linergb[i - kCostNum][1];
fig->linergb[i][2] = fig->linergb[i - kCostNum][2];
}
fig = &sim->figconstraint;
for (int i=kConstraintNum; i<mjMAXLINE; i++) {
fig->linergb[i][0] = fig->linergb[i - kConstraintNum][0];
fig->linergb[i][1] = fig->linergb[i - kConstraintNum][1];
fig->linergb[i][2] = fig->linergb[i - kConstraintNum][2];
}
// legends
mju::strcpy_arr(sim->figconstraint.linename[0], "total");
mju::strcpy_arr(sim->figconstraint.linename[1], "active");
@@ -272,54 +290,76 @@ void InitializeProfiler(mj::Simulate* sim) {
// update profiler figures
void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) {
// update constraint figure
sim->figconstraint.linepnt[0] = mjMIN(mjMIN(d->solver_iter, mjNSOLVER), mjMAXLINEPNT);
for (int i=1; i<5; i++) {
sim->figconstraint.linepnt[i] = sim->figconstraint.linepnt[0];
}
if (m->opt.solver==mjSOL_PGS) {
sim->figconstraint.linepnt[3] = 0;
sim->figconstraint.linepnt[4] = 0;
}
if (m->opt.solver==mjSOL_CG) {
sim->figconstraint.linepnt[4] = 0;
}
for (int i=0; i<sim->figconstraint.linepnt[0]; i++) {
// x
sim->figconstraint.linedata[0][2*i] = i;
sim->figconstraint.linedata[1][2*i] = i;
sim->figconstraint.linedata[2][2*i] = i;
sim->figconstraint.linedata[3][2*i] = i;
sim->figconstraint.linedata[4][2*i] = i;
// reset lines in Constraint and Cost figures
memset(sim->figconstraint.linepnt, 0, mjMAXLINE*sizeof(int));
memset(sim->figcost.linepnt, 0, mjMAXLINE*sizeof(int));
// y
sim->figconstraint.linedata[0][2*i+1] = d->nefc;
sim->figconstraint.linedata[1][2*i+1] = d->solver[i].nactive;
sim->figconstraint.linedata[2][2*i+1] = d->solver[i].nchange;
sim->figconstraint.linedata[3][2*i+1] = d->solver[i].neval;
sim->figconstraint.linedata[4][2*i+1] = d->solver[i].nupdate;
}
// number of islands that have diagnostics
int nisland = mjMIN(d->solver_nisland, mjNISLAND);
// update cost figure
sim->figcost.linepnt[0] = mjMIN(mjMIN(d->solver_iter, mjNSOLVER), mjMAXLINEPNT);
for (int i=1; i<3; i++) {
sim->figcost.linepnt[i] = sim->figcost.linepnt[0];
}
if (m->opt.solver==mjSOL_PGS) {
sim->figcost.linepnt[1] = 0;
sim->figcost.linepnt[2] = 0;
}
// iterate over islands
for (int k=0; k < nisland; k++) {
// ==== update Constraint ("Counts") figure
for (int i=0; i<sim->figcost.linepnt[0]; i++) {
// x
sim->figcost.linedata[0][2*i] = i;
sim->figcost.linedata[1][2*i] = i;
sim->figcost.linedata[2][2*i] = i;
// number of points to plot, starting line
int npoints = mjMIN(mjMIN(d->solver_niter[k], mjNSOLVER), mjMAXLINEPNT);
int start = kConstraintNum * k;
// y
sim->figcost.linedata[0][2*i+1] = mju_log10(mju_max(mjMINVAL, d->solver[i].improvement));
sim->figcost.linedata[1][2*i+1] = mju_log10(mju_max(mjMINVAL, d->solver[i].gradient));
sim->figcost.linedata[2][2*i+1] = mju_log10(mju_max(mjMINVAL, d->solver[i].lineslope));
sim->figconstraint.linepnt[start + 0] = npoints;
for (int i=1; i < kConstraintNum; i++) {
sim->figconstraint.linepnt[start + i] = npoints;
}
if (m->opt.solver == mjSOL_PGS) {
sim->figconstraint.linepnt[start + 3] = 0;
sim->figconstraint.linepnt[start + 4] = 0;
}
if (m->opt.solver == mjSOL_CG) {
sim->figconstraint.linepnt[start + 4] = 0;
}
for (int i=0; i<npoints; i++) {
// x
sim->figconstraint.linedata[start + 0][2*i] = i;
sim->figconstraint.linedata[start + 1][2*i] = i;
sim->figconstraint.linedata[start + 2][2*i] = i;
sim->figconstraint.linedata[start + 3][2*i] = i;
sim->figconstraint.linedata[start + 4][2*i] = i;
// y
int nefc = nisland == 1 ? d->nefc : d->island_efcnum[k];
sim->figconstraint.linedata[start + 0][2*i+1] = nefc;
const mjSolverStat* stat = d->solver + k*mjNSOLVER + i;
sim->figconstraint.linedata[start + 1][2*i+1] = stat->nactive;
sim->figconstraint.linedata[start + 2][2*i+1] = stat->nchange;
sim->figconstraint.linedata[start + 3][2*i+1] = stat->neval;
sim->figconstraint.linedata[start + 4][2*i+1] = stat->nupdate;
}
// update cost figure
start = kCostNum * k;
sim->figcost.linepnt[start + 0] = npoints;
for (int i=1; i<kCostNum; i++) {
sim->figcost.linepnt[start + i] = npoints;
}
if (m->opt.solver==mjSOL_PGS) {
sim->figcost.linepnt[start + 1] = 0;
sim->figcost.linepnt[start + 2] = 0;
}
for (int i=0; i<sim->figcost.linepnt[0]; i++) {
// x
sim->figcost.linedata[start + 0][2*i] = i;
sim->figcost.linedata[start + 1][2*i] = i;
sim->figcost.linedata[start + 2][2*i] = i;
// y
const mjSolverStat* stat = d->solver + k*mjNSOLVER + i;
sim->figcost.linedata[start + 0][2*i + 1] =
mju_log10(mju_max(mjMINVAL, stat->improvement));
sim->figcost.linedata[start + 1][2*i + 1] =
mju_log10(mju_max(mjMINVAL, stat->gradient));
sim->figcost.linedata[start + 2][2*i + 1] =
mju_log10(mju_max(mjMINVAL, stat->lineslope));
}
}
// get timers: total, collision, prepare, solve, other
@@ -355,14 +395,22 @@ void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) {
}
}
// get total number of iterations and nonzeros
mjtNum sqrt_nnz = 0;
int solver_niter = 0;
for (int island=0; island < nisland; island++) {
sqrt_nnz += mju_sqrt(d->solver_nnz[island]);
solver_niter += d->solver_niter[island];
}
// get sizes: nv, nbody, nefc, sqrt(nnz), ncont, iter
float sdata[6] = {
static_cast<float>(m->nv),
static_cast<float>(m->nbody),
static_cast<float>(d->nefc),
static_cast<float>(mju_sqrt(d->solver_nnz)),
static_cast<float>(sqrt_nnz),
static_cast<float>(d->ncon),
static_cast<float>(d->solver_iter)
static_cast<float>(solver_niter)
};
// update figsize
@@ -497,14 +545,22 @@ void UpdateInfoText(mj::Simulate* sim, const mjModel* m, const mjData* d,
char (&content)[mj::Simulate::kMaxFilenameLength]) {
char tmp[20];
// compute solver error
// number of islands with statistics
int nisland = mjMIN(d->solver_nisland, mjNISLAND);
// compute solver error (maximum over islands)
mjtNum solerr = 0;
if (d->solver_iter) {
int ind = mjMIN(d->solver_iter-1, mjNSOLVER-1);
solerr = mju_min(d->solver[ind].improvement, d->solver[ind].gradient);
if (solerr==0) {
solerr = mju_max(d->solver[ind].improvement, d->solver[ind].gradient);
for (int i=0; i < nisland; i++) {
mjtNum solerr_i = 0;
if (d->solver_niter[i]) {
int ind = mjMIN(d->solver_niter[i], mjNSOLVER) - 1;
const mjSolverStat* stat = d->solver + i*mjNSOLVER + ind;
solerr_i = mju_min(stat->improvement, stat->gradient);
if (solerr_i==0) {
solerr_i = mju_max(stat->improvement, stat->gradient);
}
}
solerr = mju_max(solerr, solerr_i);
}
solerr = mju_log10(mju_max(mjMINVAL, solerr));
@@ -516,6 +572,12 @@ void UpdateInfoText(mj::Simulate* sim, const mjModel* m, const mjData* d,
mju::sprintf_arr(fps, "%.0f ", sim->fps_);
}
// total iterations of all islands with statistics
int solver_niter = 0;
for (int i=0; i < nisland; i++) {
solver_niter += d->solver_niter[i];
}
// prepare info text
mju::strcpy_arr(title, "Time\nSize\nCPU\nSolver \nFPS\nMemory");
mju::sprintf_arr(content,
@@ -525,7 +587,7 @@ void UpdateInfoText(mj::Simulate* sim, const mjModel* m, const mjData* d,
sim->run ?
d->timer[mjTIMER_STEP].duration / mjMAX(1, d->timer[mjTIMER_STEP].number) :
d->timer[mjTIMER_FORWARD].duration / mjMAX(1, d->timer[mjTIMER_FORWARD].number),
solerr, d->solver_iter,
solerr, solver_niter,
fps,
d->maxuse_arena/(double)(d->narena),
mju_writeNumBytes(d->narena));