tvl-depot/third_party/immer/doc/memory.rst

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

205 lines
7 KiB
ReStructuredText
Raw Normal View History

Memory management
=================
Memory management is specially important for immutable data
structures. This is mainly due to:
#. In order to preserve the old value, new memory has to be allocated
to store the new data whenever a container is manipulated. Thus,
more allocations are performed *when changing* than with traditional
mutable data structures.
#. In order to support *structural sharing* transparently, some kind
of garbage collection mechanism is required. Passing immutable
data structures around is, internally, just passing references,
thus the system needs to figure out somehow when old values are not
referenced anymore and should be deallocated.
Thus, most containers in this library can be customized via policies_
in order to use different *allocation* and *garbage collection*
strategies.
.. doxygentypedef:: immer::default_memory_policy
.. doxygentypedef:: immer::default_heap_policy
.. doxygentypedef:: immer::default_refcount_policy
.. _policies: https://en.wikipedia.org/wiki/Policy-based_design
.. _memory policy:
Memory policy
-------------
.. doxygenstruct:: immer::memory_policy
:members:
:undoc-members:
.. _gc:
Example: tracing garbage collection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It is note worthy that all aspects of a
:cpp:class:`immer::memory_policy` are not completely orthogonal.
Let's say you want to use a `tracing garbage collector`_. Actually, we
already provide :cpp:class:`a heap <immer::gc_heap>` that interfaces
with the `Boehm's conservative garbage collector`_. Chunks of memory
allocated with this heap do not need to be deallocated, instead, after
a certain number of allocations, the heap itself will scan the stack
and all allocated memory to find references to other blocks of memory.
The memory that is not referenced anymore is automatically *freed*.
Thus, no reference counting mechanism is needed, and it makes no sense
to use this heap with anything else than the
:cpp:class:`immer::no_refcount_policy`. Also, a big object can be
separated in parts that contain pointers and parts that do not, which
make scanning references faster. So it makes most sense to use
``prefer_fewer_bigger_objects = false``.
.. note:: There are few considerations to note when using
:cpp:class:`gc_heap` with containers. Please make sure to
read :cpp:class:`its documentation section <immer::gc_heap>`
before using it.
.. literalinclude:: ../example/vector/gc.cpp
:language: c++
:start-after: example/start
:end-before: example/end
.. _boehm's conservative garbage collector: https://github.com/ivmai/bdwgc
.. _tracing garbage collector: https://en.wikipedia.org/wiki/Tracing_garbage_collection
Heaps
-----
A **heap policy** is a `metafunction class`_ that given the sizes of
the objects that we want to allocate, it *returns* a heap that can
allocate objects of those sizes.
.. _metafunction class: http://www.boost.org/doc/libs/1_62_0/libs/mpl/doc/refmanual/metafunction-class.html
A **heap** is a type with a methods ``void* allocate(std::size_t)``
and ``void deallocate(void*)`` that return and release raw memory.
For a canonical model of this concept check the
:cpp:class:`immer::cpp_heap`.
.. note:: Currently, *heaps* can only have **global state**. Having
internal state poses conceptual problems for immutable data
structures: should a `const` method of a container modify
its internal allocator state? Should every immutable
container object have its own internal state, or new objects
made from another one just keep a reference to the allocator
of the parent?
On the other hand, having some **scoped state** does make
sense for some use-cases of immutable data structures. For
example, we might want to support variations of `region
based allocation`_. This interface might evolve to evolve
to support some kind of non-global state to accommodate
these use cases.
.. _region based allocation: https://en.wikipedia.org/wiki/Region-based_memory_management
.. admonition:: Why not use the standard allocator interface?
The standard allocator API was not designed to support different
allocation strategies, but to abstract over the underlying memory
model instead. In C++11 the situation improves, but the new API is
*stateful*, posing various challenges as described in the previous
note. So far it was easier to provide our own allocation
interface. In the future, we will provide adaptors so
standard-compatible allocators can also be used with ``immer``.
Heap policies
~~~~~~~~~~~~~
.. doxygenstruct:: immer::heap_policy
:members:
:undoc-members:
.. doxygenstruct:: immer::free_list_heap_policy
Standard heap
~~~~~~~~~~~~~
.. doxygenstruct:: immer::cpp_heap
:members:
Malloc heap
~~~~~~~~~~~
.. doxygenstruct:: immer::malloc_heap
:members:
Garbage collected heap
~~~~~~~~~~~~~~~~~~~~~~
.. doxygenclass:: immer::gc_heap
Heap adaptors
~~~~~~~~~~~~~
Inspired by `Andrei Alexandrescu's talk on allocators <allocation
vexation>`_ and `Emery Berger's heap layers <heap layers>`_ we provide
allocator adaptors that can be combined using C++ mixins. These
enable building more complex allocator out of simpler strategies, or
provide application specific optimizations on top of general
allocators.
.. _allocation vexation: https://www.youtube.com/watch?v=LIb3L4vKZ7U
.. _heap layers: https://github.com/emeryberger/Heap-Layers
.. doxygenstruct:: immer::with_data
.. doxygenstruct:: immer::free_list_heap
.. doxygenstruct:: immer::thread_local_free_list_heap
.. doxygenstruct:: immer::unsafe_free_list_heap
.. doxygenstruct:: immer::identity_heap
.. doxygenstruct:: immer::debug_size_heap
.. doxygenstruct:: immer::split_heap
.. _rc:
Reference counting
------------------
`Reference counting`_ is the most commonly used garbage collection
strategy for C++. It can be implemented non-intrusively, in a way
orthogonal to the allocation strategy. It is deterministic, playing
well with RAII_.
A `memory policy`_ can provide a reference counting strategy that
containers can use to track their contents.
.. _reference counting: https://en.wikipedia.org/wiki/Reference_counting
.. _raii: https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
.. doxygenstruct:: immer::refcount_policy
.. doxygenstruct:: immer::unsafe_refcount_policy
.. doxygenstruct:: immer::no_refcount_policy
Transience
----------
In order to support `transients`, it is needed to provide a mechanism
to track the ownership of the data allocated inside the container.
This concept is encapsulated in *transience policies*.
Note that when :ref:`reference counting <rc>` is available, no such mechanism is
needed. However, when :ref:`tracing garbage collection<gc>` is used instead,
a special policy has to be provided. Otherwise, the transient API is
still available, but it will perform poorly, since it won't be able to
mutate any data in place.
.. doxygenstruct:: immer::no_transience_policy
.. doxygenstruct:: immer::gc_transience_policy