Make benchmarks and viewer more accessible.

- Drop google_benchmark module dependency, wasn't really using it.
- Move benchmark models into test_data
- Rename benchmark => testspeed, make default output more like MJ testspeed.
- Update viewer to use new io functions and add get_data_into.
- Add "mjx-testspeed" and "mjx-viewer" bin scripts

PiperOrigin-RevId: 601277135
Change-Id: I782f666c58a96ac1a0a1c5ffc8ecf29d9fcd810f
This commit is contained in:
Erik Frey
2024-01-24 17:03:13 -08:00
committed by Copybara-Service
parent c8e839a666
commit e59605135a
47 changed files with 204 additions and 158 deletions
+123
View File
@@ -0,0 +1,123 @@
# Copyright 2023 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.
# ==============================================================================
"""Run benchmarks on various devices."""
import time
from typing import Sequence, Tuple
from absl import app
from absl import flags
from etils import epath
import jax
import mujoco
from mujoco import mjx
FLAGS = flags.FLAGS
flags.DEFINE_string('mjcf', None, 'path to model', required=True)
flags.DEFINE_integer('nstep', 1000, 'number of steps per rollout')
flags.DEFINE_integer('batch_size', 1024, 'number of parallel rollouts')
flags.DEFINE_integer('unroll', 1, 'loop unroll length')
flags.DEFINE_enum('solver', 'cg', ['cg', 'newton'], 'constraint solver')
flags.DEFINE_integer('iterations', 1, 'number of solver iterations')
flags.DEFINE_integer('ls_iterations', 4, 'number of linesearch iterations')
flags.DEFINE_enum('output', 'text', ['text', 'tsv'], 'format to print results')
def _measure(fn, *args) -> Tuple[float, float]:
"""Reports jit time and op time for a function."""
beg = time.perf_counter()
compiled_fn = fn.lower(*args).compile()
end = time.perf_counter()
jit_time = end - beg
beg = time.perf_counter()
result = compiled_fn(*args)
jax.block_until_ready(result)
end = time.perf_counter()
run_time = end - beg
return jit_time, run_time
def _main(argv: Sequence[str]):
"""Benchmark a model."""
f = epath.resource_path('mujoco.mjx') / 'test_data' / FLAGS.mjcf
m = mujoco.MjModel.from_xml_path(f.as_posix())
m.opt.solver = {
'cg': mujoco.mjtSolver.mjSOL_CG,
'newton': mujoco.mjtSolver.mjSOL_NEWTON,
}[FLAGS.solver.lower()]
m.opt.iterations = FLAGS.iterations
m.opt.ls_iterations = FLAGS.ls_iterations
m = mjx.put_model(m)
if FLAGS.output == 'text':
print(f"Rolling out {FLAGS.nstep} steps at dt = {m.opt.timestep:.3f}...")
@jax.pmap
def init(key):
key = jax.random.split(key, FLAGS.batch_size // jax.device_count())
@jax.vmap
def random_init(key):
d = mjx.make_data(m)
qvel = 0.01 * jax.random.normal(key, shape=(m.nv,))
d = d.replace(qvel=qvel)
return d
return random_init(key)
key = jax.random.split(jax.random.key(0), jax.device_count())
d = init(key)
jax.block_until_ready(d)
@jax.pmap
def unroll(d):
@jax.vmap
def step(d, _):
d = mjx.step(m, d)
return d, None
d, _ = jax.lax.scan(step, d, None, length=FLAGS.nstep, unroll=FLAGS.unroll)
return d
jit_time, run_time = _measure(unroll, d)
steps = FLAGS.nstep * FLAGS.batch_size
if FLAGS.output == 'text':
print(f"""
Summary for {FLAGS.batch_size} parallel rollouts
Total JIT time: {jit_time:.2f} s
Total simulation time: {run_time:.2f} s
Total steps per second: { steps / run_time:.0f}
Total realtime factor: { steps * m.opt.timestep / run_time:.2f} x
Total time per step: { 1e6 * run_time / steps:.2f} µs""")
elif FLAGS.output == 'tsv':
name = argv[0].split('/')[-1].replace('testspeed_', '')
print(f"{name}\tjit: {jit_time:.2f}s\tsteps/second: {steps / run_time:.0f}")
def main():
app.run(_main)
if __name__ == '__main__':
main()