mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-03 10:31:37 -08:00
380 lines
14 KiB
ReStructuredText
380 lines
14 KiB
ReStructuredText
.. index::
|
||
single: allocation; segregated-fit
|
||
single: free list; segregated
|
||
single: segregated allocation cache
|
||
single: segregated free list
|
||
|
||
.. _topic-cache:
|
||
|
||
Segregated allocation caches
|
||
============================
|
||
|
||
A :dfn:`segregated allocation cache` is a data structure that can be
|
||
attached to any :term:`manually managed <manual memory management>`
|
||
:term:`pool`, that maintains a :term:`segregated free list`, that is,
|
||
a reserve of free blocks segregated by size.
|
||
|
||
Create a segregated allocation cache by preparing an array of
|
||
structures of type :c:type:`mps_sac_class_s` and passing them to
|
||
:c:func:`mps_sac_create`. The values in these structures are hints as
|
||
to the size of the blocks, the number of blocks of each size, and the
|
||
relative frequency of allocations and deallocations at that size.
|
||
|
||
For example, suppose we have a pool where we expect to allocate a
|
||
small number of relatively long-lived 128-byte objects, and a large
|
||
number of relatively short-lived 8-byte objects, we might create a
|
||
cache as follows::
|
||
|
||
mps_sac_class_s classes[3] = {{8, 100, 10}, {128, 8, 1}};
|
||
mps_sac_t sac;
|
||
|
||
res = mps_sac_create(&sac, pool, sizeof classes / sizeof classes[0], classes);
|
||
if (res != MPS_RES_OK)
|
||
error("failed to create allocation cache");
|
||
|
||
Allocations through the cache (using :c:func:`mps_sac_alloc` or
|
||
:c:func:`MPS_SAC_ALLOC_FAST`) are serviced from the cache if possible,
|
||
otherwise from the pool. Similarly, deallocations through the cache
|
||
(using :c:func:`mps_sac_free` or :c:func:`MPS_SAC_FREE_FAST`) return
|
||
the block to the appopriate free list for its size. For example::
|
||
|
||
Foo *foo;
|
||
mps_addr_t p;
|
||
mps_res_t res;
|
||
|
||
res = mps_sac_alloc(&p, sac, sizeof *foo, false);
|
||
if (res != MPS_RES_OK)
|
||
error("failed to alloc foo");
|
||
foo = p;
|
||
|
||
/* use 'foo' */
|
||
|
||
mps_sac_free(sac, p, sizeof *foo);
|
||
|
||
The macros :c:func:`MPS_SAC_ALLOC_FAST` and
|
||
:c:func:`MPS_SAC_FREE_FAST` allow allocation and deallocation to be
|
||
inlined in the calling functions, in the case where a free block is
|
||
found in the cache.
|
||
|
||
.. note::
|
||
|
||
It is recommended that you deallocate a block via the same
|
||
segregated allocation cache that you allocated it from. However,
|
||
the system is more general than that, and in fact a block that was
|
||
allocated from cache A can be deallocated via cache B, provided
|
||
that:
|
||
|
||
1. the two caches are attached to the same pool; and
|
||
|
||
2. the two caches have the same :dfn:`class structure`, that is,
|
||
they were created by passing identical arrays of :term:`size
|
||
classes`.
|
||
|
||
.. warning::
|
||
|
||
Segregated allocation caches work poorly with debugging pool
|
||
classes: the debugging checks only happen when blocks are moved
|
||
between the cache and the pool.
|
||
|
||
|
||
.. index::
|
||
single: segregated allocation cache; creating
|
||
|
||
Cache interface
|
||
---------------
|
||
|
||
.. c:type:: mps_sac_t
|
||
|
||
The type of :term:`segregated allocation caches`.
|
||
|
||
|
||
.. c:macro:: MPS_SAC_CLASS_LIMIT
|
||
|
||
The number of :term:`size classes` that :c:func:`mps_sac_create`
|
||
is guaranteed to accept.
|
||
|
||
More might be accepted: in fact, there might not be any limit in
|
||
the implementation on the maximum number of size classes, but if
|
||
you specify more than this many, you should be prepared to handle
|
||
the :term:`result code` :c:macro:`MPS_RES_LIMIT`.
|
||
|
||
|
||
.. c:type:: mps_sac_class_s
|
||
|
||
The type of the structure describing a :term:`size class` in a
|
||
:term:`segregated allocation cache`. ::
|
||
|
||
typedef struct mps_sac_class_s {
|
||
size_t mps_block_size;
|
||
size_t mps_cached_count;
|
||
unsigned mps_frequency;
|
||
} mps_sac_class_s;
|
||
|
||
An array of these structures must be passed to
|
||
:c:func:`mps_sac_create` when creating a segregated allocation
|
||
cache.
|
||
|
||
``mps_block_size`` is the maximum :term:`size` of any :term:`block`
|
||
in this size class. It must be a multiple of the alignment of the
|
||
:term:`alignment` of the :term:`pool` to which the cache belongs.
|
||
|
||
``mps_cached_count`` is the number of blocks of this size class to
|
||
cache. It is advice to the MPS on how many blocks to cache, not an
|
||
absolute limit. The cache policy tries to accommodate fluctuations
|
||
in the population and minimize the cost of responding to client
|
||
requests; the purpose of this parameter is to limit how much
|
||
memory the :term:`client program` is willing to set aside for this
|
||
purpose. However, a ``cached_count`` of zero prevents any caching of
|
||
blocks falling into that size class.
|
||
|
||
``mps_frequency`` is a number that describes the frequency of
|
||
requests (allocation and deallocation combined) in this size class
|
||
relative to the other size classes in the cache.
|
||
|
||
|
||
.. c:function:: mps_res_t mps_sac_create(mps_sac_t *sac_o, mps_pool_t pool, size_t classes_count, mps_sac_class_s *classes)
|
||
|
||
Create a :term:`segregated allocation cache` for a :term:`pool`.
|
||
|
||
``sac_o`` points to a location that will hold the address of the
|
||
segregated allocation cache.
|
||
|
||
``pool`` is the pool the cache is attached to.
|
||
|
||
``classes_count`` is the number of :term:`size classes` in the
|
||
cache.
|
||
|
||
``classes`` points to an array describing the size classes in the
|
||
cache.
|
||
|
||
Returns :c:macro:`MPS_RES_OK` if the segregated allocation cache
|
||
is created successfully. Returns :c:macro:`MPS_RES_MEMORY` or
|
||
:c:macro:`MPS_RES_COMMIT_LIMIT` when it fails to allocate memory
|
||
for the internal cache structure. Returns :c:macro:`MPS_RES_LIMIT`
|
||
if you ask for too many size classes: in this case, combine some
|
||
small adjacent classes.
|
||
|
||
After this function returns, the array of size classes pointed to
|
||
be ``classes`` is no longer needed and may be discarded. The
|
||
segregated allocation cache pointed to by ``sac_o`` persists until
|
||
it is destroyed by calling :c:func:`mps_sac_destroy`.
|
||
|
||
This function creates an allocation cache whose :term:`free list`
|
||
is segregated into the given size classes. The cache can get more
|
||
memory from the given pool, or return memory to it.
|
||
|
||
Segregated allocation caches can be associated with any pool that
|
||
supports :term:`manual <manual memory management>` allocation with
|
||
the functions :c:func:`mps_alloc` and :c:func:`mps_free`.
|
||
|
||
The size classes are described by an array of element type
|
||
:c:type:`mps_sac_class_s`. This array is used to initialize the
|
||
segregated allocation cache, and is not needed after
|
||
:c:func:`mps_sac_create` returns. The following constraints apply
|
||
to the array:
|
||
|
||
* You must specify at least one size class.
|
||
|
||
* All size classes must have different sizes.
|
||
|
||
* The size classes must be given in the order of increasing size.
|
||
|
||
* The smallest size must be at least as large as ``sizeof(void *)``.
|
||
|
||
* Each size must be a multiple of the :term:`alignment` of the
|
||
pool.
|
||
|
||
* There might be a limit on how many classes can be described, but
|
||
it will be at least :c:macro:`MPS_SAC_CLASS_LIMIT`.
|
||
|
||
The MPS automatically provides an "overlarge" size class for
|
||
arbitrarily large allocations above the largest size class
|
||
described. Allocations falling into the overlarge size class are
|
||
not cached.
|
||
|
||
Any allocations whose size falls between two size classes are
|
||
allocated from the larger size class.
|
||
|
||
.. note::
|
||
|
||
Too many size classes will slow down allocation; too few size
|
||
classes waste more space in internal fragmentation. It is
|
||
assumed that overlarge allocations are rare; otherwise, you
|
||
would add another size class for them, or even create separate
|
||
allocation caches or pools for them.
|
||
|
||
|
||
.. c:function:: void mps_sac_destroy(mps_sac_t sac)
|
||
|
||
Destroy a :term:`segregated allocation cache`.
|
||
|
||
``sac`` is the segregated allocation cache to destroy.
|
||
|
||
Returns all memory in the cache to the associated :term:`pool`.
|
||
The pool might then return some memory to the :term:`arena`, but
|
||
that's up to the pool's usual policy.
|
||
|
||
Destroying the cache has no effect on blocks allocated through it.
|
||
|
||
|
||
.. c:function:: void mps_sac_flush(mps_sac_t sac)
|
||
|
||
Flush a :term:`segregated allocation cache`, returning all memory
|
||
held in it to the associated :term:`pool`.
|
||
|
||
``sac`` is the segregated allocation cache to flush.
|
||
|
||
This is something that you'd typically do when you know you won't
|
||
be using the segregated allocation cache for awhile, but want to
|
||
hold on to the cache itself. Destroying a cache has the effect of
|
||
flushing it.
|
||
|
||
Flushing the segregated allocation cache might well cause the pool
|
||
to return some memory to the :term:`arena`, but that's up to the
|
||
pool's usual policy.
|
||
|
||
.. note::
|
||
|
||
The MPS might also decide to take memory from the segregated
|
||
allocation cache without the :term:`client program` requesting
|
||
a flush.
|
||
|
||
.. note::
|
||
|
||
The :term:`client program` is responsible for synchronizing
|
||
the access to the cache, but if the cache decides to access
|
||
the pool, the MPS will properly synchronize with any other
|
||
:term:`threads` that might be accessing the same
|
||
pool.
|
||
|
||
|
||
.. index::
|
||
pair: segregated allocation cache; allocation
|
||
|
||
Allocation interface
|
||
--------------------
|
||
|
||
.. c:function:: mps_res_t mps_sac_alloc(mps_addr_t *p_o, mps_sac_t sac, size_t size, mps_bool_t has_reservoir_permit)
|
||
|
||
Allocate a :term:`block` using a :term:`segregated allocation
|
||
cache`. If no suitable block exists in the cache, ask for more
|
||
memory from the associated :term:`pool`.
|
||
|
||
``p_o`` points to a location that will hold the address of the
|
||
allocated block.
|
||
|
||
``sac`` is the segregated allocation cache.
|
||
|
||
``size`` is the :term:`size` of the block to allocate. It does not
|
||
have to be one of the :term:`size classes` of the cache; nor does
|
||
it have to be aligned.
|
||
|
||
``has_reservoir_permit`` is obsolete. Pass false.
|
||
|
||
Returns :c:macro:`MPS_RES_OK` if successful: in this case the
|
||
address of the allocated block is ``*p_o``. The allocated block
|
||
can be larger than requested. Blocks not matching any size class
|
||
are allocated from the next largest class, and blocks larger than
|
||
the largest size class are simply allocated at the requested size
|
||
(rounded up to alignment, as usual).
|
||
|
||
Returns :c:macro:`MPS_RES_MEMORY` if there wasn't enough memory,
|
||
:c:macro:`MPS_RES_COMMIT_LIMIT` if the :term:`commit limit` was
|
||
exceeded, or :c:macro:`MPS_RES_RESOURCE` if it ran out of
|
||
:term:`virtual memory`.
|
||
|
||
.. note::
|
||
|
||
1. There's also a macro :c:func:`MPS_SAC_ALLOC_FAST` that does
|
||
the same thing. The macro is faster, but generates more
|
||
code and does less checking.
|
||
|
||
2. The :term:`client program` is responsible for synchronizing
|
||
the access to the cache, but if the cache decides to access
|
||
the pool, the MPS will properly synchronize with any other
|
||
:term:`threads` that might be accessing the same pool.
|
||
|
||
3. Blocks allocated through a segregated allocation cache
|
||
should only be freed through a segregated allocation cache
|
||
with the same class structure. Calling :c:func:`mps_free`
|
||
on them can cause :term:`memory leaks`, because the size of
|
||
the block might be larger than you think. Naturally, the
|
||
cache must also be attached to the same pool.
|
||
|
||
4. It is tempting to call :c:func:`mps_sac_alloc` with a cast
|
||
from the desired pointer type to ``mps_addr_t *``, like
|
||
this::
|
||
|
||
my_object *obj;
|
||
res = mps_alloc((mps_addr_t *)&obj, sac, sizeof *obj, 0);
|
||
if (res != MPS_RES_OK)
|
||
error(...);
|
||
|
||
but this is :term:`type punning`, and its behaviour is not
|
||
defined in ANSI/ISO Standard C. See
|
||
:ref:`topic-interface-pun` for more details.
|
||
|
||
|
||
.. c:function:: MPS_SAC_ALLOC_FAST(mps_res_t res_v, mps_addr_t *p_v, mps_sac_t sac, size_t size, mps_bool_t has_reservoir_permit)
|
||
|
||
A macro alternative to :c:func:`mps_sac_alloc`. It is faster than
|
||
the function, but generates more code, does less checking.
|
||
|
||
It takes an lvalue ``p_v`` which is assigned the address of the
|
||
allocated block (instead of a pointer to a location to store
|
||
it). It takes an additional first argument, the lvalue ``res_v``,
|
||
which is assigned the :term:`result code`.
|
||
|
||
.. note::
|
||
|
||
:c:func:`MPS_SAC_ALLOC_FAST` may evaluate its arguments
|
||
multiple times.
|
||
|
||
|
||
.. c:function:: void mps_sac_free(mps_sac_t sac, mps_addr_t p, size_t size)
|
||
|
||
Free a :term:`block` using a :term:`segregated allocation
|
||
cache`. If the cache would become too full, some blocks may be
|
||
returned to the associated :term:`pool`.
|
||
|
||
``sac`` is the segregated allocation cache.
|
||
|
||
``p`` points to the block to be freed. This block must have been
|
||
allocated through a segregated allocation cache with the same
|
||
class structure, attached to the same pool. (Usually, you'd use
|
||
the same cache to allocate and deallocate a block, but the MPS is
|
||
more flexible.)
|
||
|
||
``size`` is the :term:`size` of the block. It should be the size
|
||
that was specified when the block was allocated (the cache knows
|
||
what the real size of the block is).
|
||
|
||
.. note::
|
||
|
||
The :term:`client program` is responsible for synchronizing
|
||
the access to the cache, but if the cache decides to access
|
||
the pool, the MPS will properly synchronize with any other
|
||
:term:`threads` that might be accessing the same
|
||
pool.
|
||
|
||
.. note::
|
||
|
||
There's also a macro :c:func:`MPS_SAC_FREE_FAST` that does the
|
||
same thing. The macro is faster, but generates more code and
|
||
does no checking.
|
||
|
||
.. note::
|
||
|
||
:c:func:`mps_sac_free` does very little checking: it's
|
||
optimized for speed. :term:`Double frees` and
|
||
other mistakes will only be detected when the cache is flushed
|
||
(either by calling :c:func:`mps_sac_flush` or automatically),
|
||
and may not be detected at all, if intervening operations have
|
||
obscured symptoms.
|
||
|
||
|
||
.. c:function:: MPS_SAC_FREE_FAST(mps_sac_t sac, mps_addr_t p, size_t size)
|
||
|
||
A macro alternative to :c:func:`mps_sac_free` that is faster than
|
||
the function but does no checking. The arguments are identical to
|
||
the function.
|