Version 2.1.1: Binaries for ARM64, proper macOS bundles, minor bugfixes.
Closes #2 Closes #42 Closes #59 PiperOrigin-RevId: 416794598 Change-Id: I1306df1127d6acecf2323873c0da0910f35d31a0
This commit is contained in:
Regular → Executable
+159
-133
@@ -12,15 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "mujoco.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
#include "mujoco.h"
|
||||
|
||||
|
||||
// model and per-thread data
|
||||
@@ -35,161 +33,189 @@ double simtime[64];
|
||||
|
||||
|
||||
// timer
|
||||
chrono::system_clock::time_point tm_start;
|
||||
mjtNum gettm(void)
|
||||
{
|
||||
chrono::duration<double> elapsed = chrono::system_clock::now() - tm_start;
|
||||
return elapsed.count();
|
||||
std::chrono::system_clock::time_point tm_start;
|
||||
mjtNum gettm(void) {
|
||||
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - tm_start;
|
||||
return elapsed.count();
|
||||
}
|
||||
|
||||
|
||||
// deallocate and print message
|
||||
int finish(const char* msg = NULL, mjModel* m = NULL)
|
||||
{
|
||||
// deallocate model
|
||||
if( m )
|
||||
mj_deleteModel(m);
|
||||
int finish(const char* msg = NULL, mjModel* m = NULL) {
|
||||
// deallocate model
|
||||
if (m) {
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
// print message
|
||||
if( msg )
|
||||
printf("%s\n", msg);
|
||||
// print message
|
||||
if (msg) {
|
||||
std::printf("%s\n", msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// thread function
|
||||
void simulate(int id, int nstep)
|
||||
{
|
||||
// clear statistics
|
||||
contacts[id] = 0;
|
||||
constraints[id] = 0;
|
||||
void simulate(int id, int nstep, mjtNum ctrlnoise) {
|
||||
// clear statistics
|
||||
contacts[id] = 0;
|
||||
constraints[id] = 0;
|
||||
|
||||
// run and time
|
||||
double start = gettm();
|
||||
for( int i=0; i<nstep; i++ )
|
||||
{
|
||||
// advance simulation
|
||||
mj_step(m, d[id]);
|
||||
// run and time
|
||||
double start = gettm();
|
||||
for (int i=0; i<nstep; i++) {
|
||||
// inject pseuso-random control noise
|
||||
if (ctrlnoise)
|
||||
for (int j=0; j<m->nu; j++) {
|
||||
mjtNum center = 0.0;
|
||||
mjtNum radius = 1.0;
|
||||
mjtNum* range = m->actuator_ctrlrange + 2*j;
|
||||
if (m->actuator_ctrllimited[j]) {
|
||||
center = (range[1] + range[0]) / 2;
|
||||
radius = (range[1] - range[0]) / 2;
|
||||
}
|
||||
radius *= ctrlnoise;
|
||||
d[id]->ctrl[j] = center + radius * (2*mju_Halton(i, j+2) - 1);
|
||||
}
|
||||
|
||||
// accumulate statistics
|
||||
contacts[id] += d[id]->ncon;
|
||||
constraints[id] += d[id]->nefc;
|
||||
}
|
||||
simtime[id] = gettm() - start;
|
||||
// advance simulation
|
||||
mj_step(m, d[id]);
|
||||
|
||||
// accumulate statistics
|
||||
contacts[id] += d[id]->ncon;
|
||||
constraints[id] += d[id]->nefc;
|
||||
}
|
||||
simtime[id] = gettm() - start;
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
// print help if arguments are missing
|
||||
if( argc<3 || argc>5 )
|
||||
return finish("\n Usage: testspeed modelfile nstep [nthread [profile]]\n");
|
||||
int main(int argc, const char** argv) {
|
||||
// print help if arguments are missing
|
||||
if (argc<2 || argc>6) {
|
||||
return finish("\n Usage: testspeed modelfile [nstep nthread ctrlnoise profile]\n");
|
||||
}
|
||||
|
||||
// read nstep and nthread
|
||||
int nstep = 0, nthread = 0, profile = 0;
|
||||
if( sscanf(argv[2], "%d", &nstep)!=1 || nstep<=0 )
|
||||
return finish("Invalid nstep argument");
|
||||
if( argc>3 )
|
||||
if( sscanf(argv[3], "%d", &nthread)!=1 )
|
||||
return finish("Invalid nthread argument");
|
||||
if( argc>4 )
|
||||
if( sscanf(argv[4], "%d", &profile)!=1 )
|
||||
return finish("Invalid profile argument");
|
||||
|
||||
// clamp nthread to [1, 64]
|
||||
nthread = mjMAX(1, mjMIN(64, nthread));
|
||||
|
||||
// get filename, determine file type
|
||||
std::string filename(argv[1]);
|
||||
bool binary = (filename.find(".mjb")!=std::string::npos);
|
||||
|
||||
// load model
|
||||
char error[1000] = "Could not load binary model";
|
||||
if( binary )
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
else
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
if( !m )
|
||||
return finish(error);
|
||||
|
||||
// make per-thread data
|
||||
int testkey = mj_name2id(m, mjOBJ_KEY, "test");
|
||||
for( int id=0; id<nthread; id++ )
|
||||
{
|
||||
d[id] = mj_makeData(m);
|
||||
if( !d[id] )
|
||||
return finish("Could not allocate mjData", m);
|
||||
|
||||
// init to keyframe "test" if present
|
||||
if( testkey>=0 )
|
||||
{
|
||||
mju_copy(d[id]->qpos, m->key_qpos + testkey*m->nq, m->nq);
|
||||
mju_copy(d[id]->qvel, m->key_qvel + testkey*m->nv, m->nv);
|
||||
mju_copy(d[id]->act, m->key_act + testkey*m->na, m->na);
|
||||
}
|
||||
// read arguments
|
||||
int nstep = 10000, nthread = 0, profile = 0;
|
||||
// inject small noise by default, to avoid fixed contact state
|
||||
mjtNum ctrlnoise = 0.01;
|
||||
if (argc>2)
|
||||
if (std::sscanf(argv[2], "%d", &nstep)!=1 || nstep<=0) {
|
||||
return finish("Invalid nstep argument");
|
||||
}
|
||||
if (argc>3)
|
||||
if (std::sscanf(argv[3], "%d", &nthread)!=1) {
|
||||
return finish("Invalid nthread argument");
|
||||
}
|
||||
if (argc>4)
|
||||
if (std::sscanf(argv[4], "%lf", &ctrlnoise)!=1) {
|
||||
return finish("Invalid ctrlnoise argument");
|
||||
}
|
||||
if (argc>5)
|
||||
if (std::sscanf(argv[5], "%d", &profile)!=1) {
|
||||
return finish("Invalid profile argument");
|
||||
}
|
||||
|
||||
// install timer callback for profiling if requested
|
||||
tm_start = chrono::system_clock::now();
|
||||
if( profile )
|
||||
mjcb_time = gettm;
|
||||
// clamp ctrlnoise to [0.0, 1.0]
|
||||
ctrlnoise = mjMAX(0.0, mjMIN(ctrlnoise, 1.0));
|
||||
|
||||
// print start
|
||||
if( nthread>1 )
|
||||
printf("\nRunning %d steps per thread at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
else
|
||||
printf("\nRunning %d steps at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
// clamp nthread to [1, 64]
|
||||
nthread = mjMAX(1, mjMIN(64, nthread));
|
||||
|
||||
// run simulation, record total time
|
||||
thread th[64];
|
||||
double starttime = gettm();
|
||||
for( int id=0; id<nthread; id++ )
|
||||
th[id] = thread(simulate, id, nstep);
|
||||
for( int id=0; id<nthread; id++ )
|
||||
th[id].join();
|
||||
double tottime = gettm() - starttime;
|
||||
// get filename, determine file type
|
||||
std::string filename(argv[1]);
|
||||
bool binary = (filename.find(".mjb")!=std::string::npos);
|
||||
|
||||
// all-thread summary
|
||||
if( nthread>1 )
|
||||
{
|
||||
printf("Summary for all %d threads\n\n", nthread);
|
||||
printf(" Total simulation time : %.2f s\n", tottime);
|
||||
printf(" Total steps per second : %.0f\n", nthread*nstep/tottime);
|
||||
printf(" Total realtime factor : %.2f x\n", nthread*nstep*m->opt.timestep/tottime);
|
||||
printf(" Total time per step : %.4f ms\n\n", 1000*tottime/(nthread*nstep));
|
||||
// load model
|
||||
char error[1000] = "Could not load binary model";
|
||||
if (binary) {
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
} else {
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
}
|
||||
if (!m) {
|
||||
return finish(error);
|
||||
}
|
||||
|
||||
printf("Details for thread 0\n\n");
|
||||
// make per-thread data
|
||||
int testkey = mj_name2id(m, mjOBJ_KEY, "test");
|
||||
for (int id=0; id<nthread; id++) {
|
||||
d[id] = mj_makeData(m);
|
||||
if (!d[id]) {
|
||||
return finish("Could not allocate mjData", m);
|
||||
}
|
||||
|
||||
// details for thread 0
|
||||
printf(" Simulation time : %.2f s\n", simtime[0]);
|
||||
printf(" Steps per second : %.0f\n", nstep/simtime[0]);
|
||||
printf(" Realtime factor : %.2f x\n", nstep*m->opt.timestep/simtime[0]);
|
||||
printf(" Time per step : %.4f ms\n\n", 1000*simtime[0]/nstep);
|
||||
printf(" Contacts per step : %d\n", contacts[0]/nstep);
|
||||
printf(" Constraints per step : %d\n", constraints[0]/nstep);
|
||||
printf(" Degrees of freedom : %d\n\n", m->nv);
|
||||
// init to keyframe "test" if present
|
||||
if (testkey>=0) {
|
||||
mju_copy(d[id]->qpos, m->key_qpos + testkey*m->nq, m->nq);
|
||||
mju_copy(d[id]->qvel, m->key_qvel + testkey*m->nv, m->nv);
|
||||
mju_copy(d[id]->act, m->key_act + testkey*m->na, m->na);
|
||||
}
|
||||
}
|
||||
|
||||
// profiler results for thread 0
|
||||
if( profile )
|
||||
{
|
||||
printf(" Profiler phase (ms per step)\n");
|
||||
mjtNum tstep = d[0]->timer[mjTIMER_STEP].duration/d[0]->timer[mjTIMER_STEP].number;
|
||||
for( int i=0; i<mjNTIMER; i++ )
|
||||
if( d[0]->timer[i].number>0 )
|
||||
{
|
||||
mjtNum istep = d[0]->timer[i].duration/d[0]->timer[i].number;
|
||||
printf(" %16s : %.5f (%6.2f %%)\n", mjTIMERSTRING[i],
|
||||
// install timer callback for profiling if requested
|
||||
tm_start = std::chrono::system_clock::now();
|
||||
if (profile) {
|
||||
mjcb_time = gettm;
|
||||
}
|
||||
|
||||
// print start
|
||||
if (nthread>1) {
|
||||
std::printf("\nRunning %d steps per thread at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
} else {
|
||||
std::printf("\nRunning %d steps at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
}
|
||||
|
||||
// run simulation, record total time
|
||||
std::thread th[64];
|
||||
double starttime = gettm();
|
||||
for (int id=0; id<nthread; id++) {
|
||||
th[id] = std::thread(simulate, id, nstep, ctrlnoise);
|
||||
}
|
||||
for (int id=0; id<nthread; id++) {
|
||||
th[id].join();
|
||||
}
|
||||
double tottime = gettm() - starttime;
|
||||
|
||||
// all-thread summary
|
||||
if (nthread>1) {
|
||||
std::printf("Summary for all %d threads\n\n", nthread);
|
||||
std::printf(" Total simulation time : %.2f s\n", tottime);
|
||||
std::printf(" Total steps per second : %.0f\n", nthread*nstep/tottime);
|
||||
std::printf(" Total realtime factor : %.2f x\n", nthread*nstep*m->opt.timestep/tottime);
|
||||
std::printf(" Total time per step : %.4f ms\n\n", 1000*tottime/(nthread*nstep));
|
||||
|
||||
std::printf("Details for thread 0\n\n");
|
||||
}
|
||||
|
||||
// details for thread 0
|
||||
std::printf(" Simulation time : %.2f s\n", simtime[0]);
|
||||
std::printf(" Steps per second : %.0f\n", nstep/simtime[0]);
|
||||
std::printf(" Realtime factor : %.2f x\n", nstep*m->opt.timestep/simtime[0]);
|
||||
std::printf(" Time per step : %.4f ms\n\n", 1000*simtime[0]/nstep);
|
||||
std::printf(" Contacts per step : %.2f\n", static_cast<float>(contacts[0])/nstep);
|
||||
std::printf(" Constraints per step : %.2f\n", static_cast<float>(constraints[0])/nstep);
|
||||
std::printf(" Degrees of freedom : %d\n\n", m->nv);
|
||||
|
||||
// profiler results for thread 0
|
||||
if (profile) {
|
||||
printf(" Profiler phase (ms per step)\n");
|
||||
mjtNum tstep = d[0]->timer[mjTIMER_STEP].duration/d[0]->timer[mjTIMER_STEP].number;
|
||||
for (int i=0; i<mjNTIMER; i++)
|
||||
if (d[0]->timer[i].number>0) {
|
||||
mjtNum istep = d[0]->timer[i].duration/d[0]->timer[i].number;
|
||||
std::printf(" %16s : %.5f (%6.2f %%)\n", mjTIMERSTRING[i],
|
||||
1000*istep, 100*istep/tstep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free per-thread data
|
||||
for( int id=0; id<nthread; id++ )
|
||||
mj_deleteData(d[id]);
|
||||
// free per-thread data
|
||||
for (int id=0; id<nthread; id++) {
|
||||
mj_deleteData(d[id]);
|
||||
}
|
||||
|
||||
// finalize
|
||||
return finish();
|
||||
// finalize
|
||||
return finish();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user