Dynamically allocate contact and efc_ arrays on a new memory arena.
- Add private function `mj_arenaAlloc`. This is used internally to allocate memory from the arena. - Add private function `mj_nefc` to count constraints. This function returns a tight upper bound on `d->nefc`. The number of counted constraints can be slightly bigger than exact `d->nefc` in the case of constraints with empty Jacobian, as when placing a frictional tendon between two world sites. - Add new `memory` attribute to the `size` XML element for specification of arena memory size. This attribute is mutually exclusive with `nstack` and `njmax` specifications, which are now deprecated (but left around for the time being for legacy compatibility). - Move `d->stack` to the end of the new arena space. The stack now grows in reverse from the end. PiperOrigin-RevId: 479341539 Change-Id: Ie019c202e0908577ffc6f833a37920858116f667
This commit is contained in:
committed by
Copybara-Service
parent
4d85a464cc
commit
58fd72f53d
@@ -4188,6 +4188,8 @@ mj_rnePostConstraint
|
||||
|
||||
RNE with complete data: compute cacc, cfrc_ext, cfrc_int.
|
||||
|
||||
.. _mj_collision:
|
||||
|
||||
mj_collision
|
||||
~~~~~~~~~~~~
|
||||
|
||||
|
||||
+20
-24
@@ -479,7 +479,7 @@ from its default.
|
||||
This flag enables the simulation of sensor noise. When disabled (which is the default) noise is not added to
|
||||
sensordata, even if the sensors specify non-zero noise amplitudes. When enabled, zero-mean Gaussian noise is added to
|
||||
the underlying deterministic sensor data. Its standard deviation is determined by the noise parameter of each sensor.
|
||||
:at:`multiccd`: :at-val:`[disable, enable], "disable"` **(experimental feature)**
|
||||
:at:`multiccd`: :at-val:`[disable, enable], "disable"` |nbsp| |nbsp| |nbsp| (experimental feature)
|
||||
This flag enables multiple-contact collision detection for geom pairs that use the general-purpose convex-convex
|
||||
collider based on :ref:`libccd <coChecking>` e.g., mesh-mesh collisions. This can be useful when the contacting geoms
|
||||
have a flat surface, and the single contact point generated by the convex-convex collider cannot accurately capture
|
||||
@@ -497,29 +497,25 @@ This element specifies size parameters that cannot be inferred from the number o
|
||||
fields of mjOption which can be modified at runtime, sizes are structural parameters and should not be modified after
|
||||
compilation.
|
||||
|
||||
:at:`njmax`: :at-val:`int, "-1"`
|
||||
This and the next two attributes specify the maximum sizes of the dynamic arrays in mjData, i.e., arrays whose
|
||||
effective length varies at runtime. This attribute specifies the maximum number of scalar constraints (or
|
||||
equivalently, rows of the constraint Jacobian) that can be handled at runtime. If the number of active constraints is
|
||||
about to exceed this maximum (usually because too many contacts become active) the extra constraints are discarded
|
||||
and a warning is generated. The number of active constraints is stored in mjData.nefc. The default setting of -1
|
||||
instructs the compiler to guess how much space to allocate (using heuristics that can be improved). This default is
|
||||
effectively an undefined state. If the user specifies a positive value, the compiler heuristics are disabled and the
|
||||
specified value is used. Modern computers have sufficient memory to handle very large models (larger than one would
|
||||
normally have the patience to simulate) so tuning this setting aggressively is not necessary. When size-related
|
||||
warnings or errors are generated, simply increase the value of the corresponding attribute.
|
||||
:at:`nconmax`: :at-val:`int, "-1"`
|
||||
This attribute specifies the maximum number of contacts (both frictional and frictionless) that can be handled at
|
||||
runtime. If the number of active contacts is about to exceed this value, the extra contacts are discarded and a
|
||||
warning is generated. The actual number of contacts is stored in mjData.ncon. If this value is negative, the compiler
|
||||
will use a heuristic to guess an appropriate number.
|
||||
:at:`nstack`: :at-val:`int, "-1"`
|
||||
This attribute specifies the size of the preallocated stack in mjData, in units of sizeof(mjtNum) which is currently
|
||||
defined as double; thus the size in bytes is 8 times larger. The custom stack is used by all MuJoCo functions that
|
||||
need dynamically allocated memory. We do not use heap memory allocation at runtime, so as to speed up processing as
|
||||
well as avoid heap fragmentation. Note that the internal allocator keeps track of how much stack space has ever been
|
||||
utilized, in the field mjData.maxstackuse of mjData. If the stack size is exceeded at runtime, MuJoCo will generate
|
||||
an error. If this value is negative, the compiler will use a heuristic to guess an appropriate number.
|
||||
:at:`memory`: :at-val:`string, "-1"`
|
||||
This attribute specifies the size of memory allocated for dynamic arrays in the ``mjData.arena`` memory space, in
|
||||
bytes. The default setting of ``-1`` instructs the compiler to guess how much space to allocate. Appending the digits
|
||||
with one of the letters {K, M, G, T, P, E} sets the unit to be {kilo, mega, giga, tera, peta, exa}-byte,
|
||||
respectively. Thus "16M" means "allocate 16 megabytes of ``arena`` memory".
|
||||
See the :ref:`Memory allocation <CSize>` section for details.
|
||||
:at:`njmax`: :at-val:`int, "-1"` |nbsp| |nbsp| |nbsp| (legacy)
|
||||
This is a deprecated legacy attribute. In versions prior to 2.3.0, it determined the maximum allowed number
|
||||
of constraints. Currently it means "allocate as much memory as would have previously been required for this number of
|
||||
constraints". Specifying both :at:`njmax` and :at:`memory` leads to an error.
|
||||
:at:`nconmax`: :at-val:`int, "-1"` |nbsp| |nbsp| |nbsp| (legacy)
|
||||
This attribute specifies the maximum number of contacts that will be generated at runtime. If the number of active
|
||||
contacts is about to exceed this value, the extra contacts are discarded and a warning is generated. This is a
|
||||
deprecated legacy attribute which prior to version 2.3.0 affected memory allocation. It is kept for backwards
|
||||
compatibillity and debugging purposes.
|
||||
:at:`nstack`: :at-val:`int, "-1"` |nbsp| |nbsp| |nbsp| (legacy)
|
||||
This is a deprecated legacy attribute. In versions prior to 2.3.0, it determined the maximum size of the
|
||||
:ref:`stack <siStack>`. Currently it is synonymous with the :at:`memory` attribute above, but is in units of
|
||||
``sizeof(mjtNum)`` rather than bytes. Specifying both :at:`nstack` and :at:`memory` leads to an error.
|
||||
:at:`nuserdata`: :at-val:`int, "0"`
|
||||
The size of the field mjData.userdata of mjData. This field should be used to store custom dynamic variables. See
|
||||
also :ref:`CUser`.
|
||||
|
||||
+14
-3
@@ -9,9 +9,17 @@ Upcoming version (not yet released)
|
||||
General
|
||||
^^^^^^^
|
||||
|
||||
.. youtube:: RHnXD6uO3Mg
|
||||
:align: right
|
||||
:height: 150px
|
||||
- The ``contact`` array and arrays prefixed with ``efc_`` in ``mjData`` were moved out of the ``buffer`` into a new
|
||||
``arena`` memory space. These arrays are no longer allocated with fixed sizes when ``mjData`` is created.
|
||||
Instead, the exact memory requirement is determined during each call to :ref:`mj_forward` (specifically,
|
||||
in :ref:`mj_collision` and :ref:`mj_makeConstraint`) and the arrays are allocated from the ``arena`` space. The
|
||||
``stack`` now also shares its available memory with ``arena``. This change reduces the memory footprint of ``mjData``
|
||||
in models that do not use the PGS solver, and will allow for significant memory reductions in the future.
|
||||
See the :ref:`Memory allocation <CSize>` section for details.
|
||||
|
||||
.. youtube:: RHnXD6uO3Mg
|
||||
:align: right
|
||||
:height: 150px
|
||||
|
||||
- Added colab notebook tutorial showing how to balance the humanoid on one leg with a Linear Quadratic Regulator. The
|
||||
notebook uses MuJoCo's native Python bindings, and includes a draft ``Renderer`` class, for easy rendering in Python.
|
||||
@@ -59,6 +67,9 @@ Python bindings
|
||||
`named accessor <https://mujoco.readthedocs.io/en/latest/python.html#named-access>`_ objects. These provide more
|
||||
Pythonic API access to ``mj_name2id`` and ``mj_id2name`` respectively.
|
||||
|
||||
- The length of ``MjData.contact`` is now ``ncon`` rather than ``nconmax``, allowing it to be straightforwardly used as
|
||||
an iterator without needing to check ``ncon``.
|
||||
|
||||
|
||||
Version 2.2.2 (September 7, 2022)
|
||||
---------------------------------
|
||||
|
||||
+51
-26
@@ -1276,6 +1276,57 @@ MuJoCo Forum for an example; the plots below are generated with that model.
|
||||
|
||||
|image18| |image19|
|
||||
|
||||
.. _CSize:
|
||||
|
||||
Memory allocation
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
MuJoCo preallocates all the memory needed at runtime in ``mjData``, and does not access the heap allocator after
|
||||
model creation. Memory in ``mjData`` is allocated by :ref:`mj_makeData` in two contiguous blocks:
|
||||
|
||||
- ``mjData.buffer`` contains fixed-size arrays.
|
||||
- ``mjData.arena`` contains dynamically-sized arrays.
|
||||
|
||||
There are two types of dynamic arrays allocated in the ``arena`` memory space.
|
||||
|
||||
- contacts and constraint-related arrays are laid out from the beginning of the ``arena``.
|
||||
- :ref:`stack <siStack>` arrays are laid out from the end of the ``arena``.
|
||||
|
||||
By allocating dynamic quantities from both sides of the ``arena`` space, variable-sized memory allocation is controlled
|
||||
by a single number: the :at:`memory` attribute of the :ref:`size <size>` MJCF element. Unlike the fixed-size arrays in
|
||||
the ``buffer``, variable-sized arrays in the arena can be ``NULL``, for example after a call to :ref:`mj_resetData`.
|
||||
When ``arena`` memory runs out, one of three things will happen, depending on the type of memory requested:
|
||||
|
||||
- If memory runs out during contact allocation, a warning will be raised and subsequent contacts will not be added in
|
||||
this step, but simulation continues as usual.
|
||||
- If memory runs out during constraint-related allocation, a warning will be raised and the constraint solver will be
|
||||
disabled in this step, but simulation continues as usual. Note that physics without the constraint solver will
|
||||
generally be very different, but allowing the simulation to continue can still be useful, e.g. during
|
||||
scene initialization when many bodies are temporarily overlapping.
|
||||
- If memory runs out during stack array allocation, a hard error will occur.
|
||||
|
||||
Unlike the size of the ``buffer``, the size of the ``arena`` cannot be pre-computed, since the number of contacts and
|
||||
stack usage is not known in advance. So how should one choose it? The following simple heuristic is currently used,
|
||||
though it may be improved in the future: enough memory is allocated for 100 contacts and 500 scalar constraints, under
|
||||
worst-case conditions. If this heuristic is insufficient, we recommend the following procedure. Increase the ``arena``
|
||||
memory significantly using the :at:`memory` attribute, and inspect the actual memory used at runtime.
|
||||
``mjData.maxuse_arena`` keeps track of the maximum ``arena`` memory utilization since the last reset. The :ref:`simulate
|
||||
<saSimulate>` viewer shows this number as a fraction of the total arena space (in the info window in the lower-left
|
||||
corner). So one can start with a large number, simulate for a while, and if the fractions are small go back to the XML
|
||||
and reduce the allocation size. Keep in mind though that memory utilization can change dramatically in the course of the
|
||||
simulation, depending on how many constraints are active and which constraint solver is used. The CG solver is the most
|
||||
memory efficient, followed by the Newton solver, while the PGS solver is the most memory intensive. When we design
|
||||
models, we usually aim for 50% utilization in the worst-case scenario encountered while exploring the model. If you only
|
||||
intend to use the CG solver, you can get away with significantly smaller arena allocation.
|
||||
|
||||
.. attention::
|
||||
|
||||
Memory allocation behaviour changed in MuJoCo 2.3.0. Before this version, the :at:`njmax`, :at:`nconmax` and
|
||||
:at:`nstack` attributes of the :ref:`size <size>` MJCF element had the semantics of maximum memory allocated for
|
||||
contacts, constraints and stack, respectively. If you are using an earlier version of MuJoCo, please switch to an
|
||||
`earlier <https://mujoco.readthedocs.io/en/2.2.2/modeling.html#model-sizes>`_ documentation version to read about the
|
||||
previous behaviour.
|
||||
|
||||
.. _Tips:
|
||||
|
||||
Tips and tricks
|
||||
@@ -1377,32 +1428,6 @@ in a visible way, and the energy fluctuates around the initial value instead of
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
.. _CSize:
|
||||
|
||||
Model sizes
|
||||
~~~~~~~~~~~
|
||||
|
||||
MuJoCo preallocates all the memory needed at runtime in mjData, and does not access the C/C++ memory manager after
|
||||
model creation. It is therefore essential to allocate enough memory. The allocation is controlled by three size
|
||||
parameters specified in the :ref:`size <size>` element, namely the stack size :at:`nstack`, the
|
||||
maximum number of contacts :at:`nconmax`, and the maximum number of scalar constraints :at:`njmax`. The default
|
||||
size settings use heuristics to allocate sufficient memory, but the true memory needs for a given model can only be
|
||||
determined during simulation. If nstack is insufficient the simulator calls mju_error and gives up. If nconmax or
|
||||
njmax are insufficient the remaining contacts or other constraints are discarded, and the simulation continues but the
|
||||
results are not as desired. If on the other hand the allocation is too large, clearing mjData with mj_reset takes
|
||||
longer, and in multi-threaded applications simulating many large models in parallel the machine could run out of
|
||||
memory, or cache performance could be adversely affected. And even if nothing bad happens, allocating a lot more
|
||||
memory than needed is just poor style.
|
||||
|
||||
So how do we know how much memory to allocate? mjData has fields maxuse_stack, maxuse_con and maxuse_efc which keep
|
||||
track of the maximum memory utilization in each category since the last reset. The code sample :ref:`simulate.cc <saSimulate>`
|
||||
shows this data as a fraction of the maximum allocation (in the info window in the lower-left corner). So one can start with
|
||||
the defaults, simulate for a while, and if the fractions are too small go back to the XML and set the allocation sizes
|
||||
explicitly. Keep in mind though that memory utilization can change dramatically in the course of the simulation,
|
||||
depending on how many constraints are active and also which constraint solver is used.
|
||||
For example if the stack size is just sufficient for the CG solver, the Newton and PGS solvers will run out of stack.
|
||||
When we design models, we usually aim for 50% utilization in the worst-case scenario encountered while exploring the
|
||||
model. If you only intend to use the CG solver, you can get away with significantly smaller stack allocation.
|
||||
|
||||
.. |image0| image:: images/modeling/impedance.png
|
||||
:width: 600px
|
||||
|
||||
+13
-20
@@ -503,10 +503,10 @@ preallocated data arrays for all intermediate results, as well as an :ref:`inter
|
||||
to allocate all necessary heap memory at the beginning of the simulation, and free it after the simulation is done, so
|
||||
that we never have to call the C memory allocation and deallocation functions during the simulation. This is done for
|
||||
speed, avoidance of memory fragmentation, future GPU portability, and ease of managing the state of the entire
|
||||
simulator during a reset. It also means however that the maximal sizes :at:`njmax`, :at:`nconmax` and
|
||||
:at:`nstack` in the XML element :ref:`size <size>`, which affect the allocation of mjData, must be
|
||||
set to sufficiently large values. If these maximal sizes are exceeded during the simulation, they are not increased
|
||||
dynamically, but instead errors or warnings are generated. See also :ref:`diagnostics <siDiagnostics>` below.
|
||||
simulator during a reset. It also means however that the maximal variable-memory allocation given by the
|
||||
:at:`memory` attribute in the :ref:`size <size>` MJCF element, which affects the allocation of ``mjData``, must be
|
||||
set to a sufficiently large value. If this maximal size is exceeded during simulation, it is not increased
|
||||
dynamically, but instead an error is generated. See also :ref:`diagnostics <siDiagnostics>` below.
|
||||
|
||||
First we must call one of the functions that allocates and initializes mjModel and returns a pointer to it. The
|
||||
available options are
|
||||
@@ -1308,22 +1308,15 @@ termination have similar order-of-magnitude as the numbers in ``mjData.fwdinv``,
|
||||
different diagnostics.
|
||||
|
||||
Since MuJoCo's runtime works with compiled models, memory is preallocated when a model is compiled or loaded. Recall the
|
||||
:ref:`size <size>` element in MJCF, which has the attributes :at:`njmax`, :at:`nconmax` and :at:`nstack`. They determine
|
||||
the maximum number of scalar constraints that can be active simultaneously, the maximum number of contact points that
|
||||
can be included in ``mjData.contact``, and the size of the internal stack. How is the user supposed to know what the
|
||||
appropriate settings are? If there were a reliable recipe we would have implemented it in the compiler, but there isn't
|
||||
one. The theoretical worst-case, namely all geoms contacting all other geoms, calls for huge allocation which is almost
|
||||
never needed in practice. So our approach is to provide default settings in MJCF which are sufficient for most models,
|
||||
and allow the user to adjust them manually with the above attributes. If the simulator runs out of stack space at
|
||||
runtime it will trigger an error. If it runs out of space for contacts or scalar constraints, it will trigger a warning
|
||||
and omit the contacts and constraints that do not fit in the allocated buffers. When such errors or warnings are
|
||||
triggered, the user should adjust the sizes. The fields ``mjData.maxuse_stack``, ``mjData.maxuse_con``,
|
||||
``mjData.maxuse_efc`` are designed to help with this adjustment. They keep track of the maximum stack allocation,
|
||||
number of contacts and number of scalar constraints respectively since the last reset. So one strategy is to make very
|
||||
large allocation, then monitor these ``maxuse_XXX`` statistics during typical simulations, and use them to reduce the
|
||||
allocation. Of course modern computers have so much memory that most users will not bother with such adjustment once
|
||||
they get rid of the out-of-memory errors and warnings, but nevertheless we provide this mechanism for the
|
||||
perfectionist.
|
||||
:at:`memory` attribute of the :ref:`size <size>` element in MJCF. It determines the preallocated space for dynamic
|
||||
arrays. How is the user supposed to know what the appropriate value is? If there were a reliable recipe we would have
|
||||
implemented it in the compiler, but there isn't one. The theoretical worst-case, namely all geoms contacting all other
|
||||
geoms, calls for huge allocation which is almost never needed in practice. Our approach is to provide default settings
|
||||
in MJCF which are sufficient for most models, and allow the user to adjust them manually with the above attribute. If
|
||||
the simulator runs out of dynamic memory at runtime it will trigger an error. When such errors are triggered, the user
|
||||
should increase :at:`memory`. The field ``mjData.maxuse_arena`` is designed to help with this adjustment. It keeps track
|
||||
of the maximum arena use since the last reset. So one strategy is to make very large allocation, then monitor
|
||||
``mjData.maxuse_memory`` statistics during typical simulations, and use it to reduce the allocation.
|
||||
|
||||
The kinetic and potential energy are computed and stored in ``mjData.energy`` when the corresponding flag in
|
||||
``mjModel.opt.enableflags`` is set. This can be used as another diagnostic. In general, simulation instability is
|
||||
|
||||
Reference in New Issue
Block a user