Files
Mujoco_WASM/src/experimental/platform/sim_profiler.cc
T
Yuval Tassa 32d7b87ba7 Improve studio 2D plots
PiperOrigin-RevId: 842260702
Change-Id: I4644b69da92b9d015eb2d140b98f68c05d31a222
2025-12-09 08:56:51 -08:00

173 lines
6.2 KiB
C++

// Copyright 2025 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "experimental/platform/sim_profiler.h"
#include <mujoco/mujoco.h>
#include <imgui.h>
#include <implot.h>
namespace mujoco::platform {
SimProfiler::SimProfiler() {
Clear();
}
void SimProfiler::Clear() {
constexpr int kProfilerMaxFrames = 200;
cpu_total_.clear();
cpu_collision_.clear();
cpu_prepare_.clear();
cpu_solve_.clear();
cpu_other_.clear();
dim_dof_.clear();
dim_body_.clear();
dim_constraint_.clear();
dim_sqrt_nnz_.clear();
dim_contact_.clear();
dim_iteration_.clear();
cpu_total_.resize(kProfilerMaxFrames, 0);
cpu_collision_.resize(kProfilerMaxFrames, 0);
cpu_prepare_.resize(kProfilerMaxFrames, 0);
cpu_solve_.resize(kProfilerMaxFrames, 0);
cpu_other_.resize(kProfilerMaxFrames, 0);
dim_dof_.resize(kProfilerMaxFrames, 0);
dim_body_.resize(kProfilerMaxFrames, 0);
dim_constraint_.resize(kProfilerMaxFrames, 0);
dim_sqrt_nnz_.resize(kProfilerMaxFrames, 0);
dim_contact_.resize(kProfilerMaxFrames, 0);
dim_iteration_.resize(kProfilerMaxFrames, 0);
}
void SimProfiler::Update(const mjModel* model, const mjData* data) {
// CPU timers.
mjtNum total = data->timer[mjTIMER_STEP].duration;
mjtNum number = static_cast<mjtNum>(data->timer[mjTIMER_STEP].number);
if (number == 0.0) {
total = data->timer[mjTIMER_FORWARD].duration;
number = static_cast<mjtNum>(data->timer[mjTIMER_FORWARD].number);
}
if (number == 0.0) {
// This can happen if the simulation is paused.
return;
}
mjtNum avg_total = total / number;
cpu_total_.erase(cpu_total_.begin());
cpu_total_.push_back(avg_total);
mjtNum collision = data->timer[mjTIMER_POS_COLLISION].duration / number;
cpu_collision_.erase(cpu_collision_.begin());
cpu_collision_.push_back(collision);
mjtNum prepare = (data->timer[mjTIMER_POS_MAKE].duration / number) +
(data->timer[mjTIMER_POS_PROJECT].duration / number);
cpu_prepare_.erase(cpu_prepare_.begin());
cpu_prepare_.push_back(prepare);
mjtNum solve = data->timer[mjTIMER_CONSTRAINT].duration / number;
cpu_solve_.erase(cpu_solve_.begin());
cpu_solve_.push_back(solve);
mjtNum other = avg_total - collision - prepare - solve;
cpu_other_.erase(cpu_other_.begin());
cpu_other_.push_back(other);
// Solver diagnostics.
mjtNum sqrt_nnz = 0;
int solver_niter = 0;
const int nisland = data->nefc ? mjMAX(1, mjMIN(data->nisland, mjNISLAND)) : 0;
for (int island=0; island < nisland; island++) {
sqrt_nnz += data->solver_nnz[island];
solver_niter += data->solver_niter[island];
}
sqrt_nnz = mju_sqrt(sqrt_nnz);
dim_dof_.erase(dim_dof_.begin());
int nv = (model->opt.enableflags & mjENBL_SLEEP) ? data->nv_awake
: model->nv;
dim_dof_.push_back(nv);
dim_body_.erase(dim_body_.begin());
int nbody = (model->opt.enableflags & mjENBL_SLEEP) ? data->nbody_awake
: model->nbody;
dim_body_.push_back(nbody);
dim_constraint_.erase(dim_constraint_.begin());
dim_constraint_.push_back(data->nefc);
dim_sqrt_nnz_.erase(dim_sqrt_nnz_.begin());
dim_sqrt_nnz_.push_back(sqrt_nnz);
dim_contact_.erase(dim_contact_.begin());
dim_contact_.push_back(data->ncon);
dim_iteration_.erase(dim_iteration_.begin());
dim_iteration_.push_back(static_cast<float>(solver_niter) / nisland);
}
void SimProfiler::CpuTimeGraph() {
if (ImPlot::BeginPlot("CPU Time", ImVec2(-1, 0), ImPlotFlags_NoMouseText)) {
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
ImPlot::SetupAxis(ImAxis_X1, "frame", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxis(ImAxis_Y1, "msec", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.2f");
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
ImPlot::SetupFinish();
ImPlot::PlotLine("total", cpu_total_.data(), cpu_total_.size(), 1,
-(int)cpu_total_.size());
ImPlot::PlotLine("prepare", cpu_prepare_.data(), cpu_prepare_.size(), 1,
-(int)cpu_prepare_.size());
ImPlot::PlotLine("solve", cpu_solve_.data(), cpu_solve_.size(), 1,
-(int)cpu_solve_.size());
ImPlot::PlotLine("collision", cpu_collision_.data(), cpu_collision_.size(),
1, -(int)cpu_collision_.size());
ImPlot::PlotLine("other", cpu_other_.data(), cpu_other_.size(), 1,
-(int)cpu_other_.size());
ImPlot::PopStyleVar();
ImPlot::EndPlot();
}
}
void SimProfiler::DimensionsGraph() {
if (ImPlot::BeginPlot("Dimensions", ImVec2(-1, 0), ImPlotFlags_NoMouseText)) {
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
ImPlot::SetupAxis(ImAxis_X1, "frame", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxis(ImAxis_Y1, "count", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.0f");
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
ImPlot::SetupFinish();
ImPlot::PlotLine("dof", dim_dof_.data(), dim_dof_.size(), 1,
-(int)dim_dof_.size());
ImPlot::PlotLine("body", dim_body_.data(), dim_body_.size(), 1,
-(int)dim_body_.size());
ImPlot::PlotLine("constraint", dim_constraint_.data(),
dim_constraint_.size(), 1, -(int)dim_constraint_.size());
ImPlot::PlotLine("sqrt(nnz)", dim_sqrt_nnz_.data(), dim_sqrt_nnz_.size(), 1,
-(int)dim_sqrt_nnz_.size());
ImPlot::PlotLine("contact", dim_contact_.data(), dim_contact_.size(), 1,
-(int)dim_contact_.size());
ImPlot::PlotLine("iteration", dim_iteration_.data(), dim_iteration_.size(),
1, -(int)dim_iteration_.size());
ImPlot::PopStyleVar();
ImPlot::EndPlot();
}
}
} // namespace mujoco::platform