1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
#ifndef INCLUDES_TARANTOOL_SMALL_SMALL_H
#define INCLUDES_TARANTOOL_SMALL_SMALL_H
/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdint.h>
#include "lib/small/mempool.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Small object allocator.
*
* The allocator consists of a collection of mempools.
*
* There are two containers of pools:
*
* pools for objects of size 8-500 bytes are stored in an array,
* where pool->objsize of each array member is a multiple of 8-16
* (value defined in STEP_SIZE constant). These are
* "stepped" pools, since pool->objsize of each next pool in the
* array differs from the previous size by a fixed step size.
*
* For example, there is a pool for size range 16-32,
* another one for 32-48, 48-64, etc. This makes the look up
* procedure for small allocations just a matter of getting an
* array index via a bit shift. All stepped pools are initialized
* when an instance of small_alloc is created.
*
* Objects of size beyond the stepped pools range (the upper limit
* is usually around 300 bytes), are stored in pools with a size
* which is a multiple of alloc_factor. alloc_factor is itself
* a configuration constant in the range (1.0, 2.0]. I.e. imagine
* alloc_factor is 1.1, then there are pools for objects of size
* 300-330, 330-363, and so on. These pools are created upon first
* allocation within given range, and stored in a red-black tree.
*
* Initially this red-black tree contains only a pool for
* alloc->object_max.
* When a request for a new allocation of sz bytes arrives
* and it can not be satisfied from a stepped pool,
* a search for a nearest factored pool is made in the tree.
*
* If, for the nearest found factored pool:
*
* pool->objsize > sz * alloc_factor,
*
* (i.e. pool object size is too big) a new factored pool is
* created and inserted into the tree.
*
* This way the tree only contains factored pools for sizes
* which are actually used by the server, and can be kept
* small.
*/
/** Basic constants of small object allocator. */
enum {
/** How many stepped pools there is. */
STEP_POOL_MAX = 32,
/** How many factored pools there can be. */
FACTOR_POOL_MAX = 256,
};
/**
* A mempool to store objects sized within one multiple of
* alloc_factor. Is a member of the red-black tree which
* contains all such pools.
*
* Example: let's assume alloc_factor is 1.1. There will be an
* instance of factor_pool for objects of size from 300 to 330,
* from 330 to 363, and so on.
*/
struct factor_pool
{
/** rb_tree entry */
rb_node(struct factor_pool) node;
/** the pool itself. */
struct mempool pool;
/**
* Objects starting from this size and up to
* pool->objsize are stored in this factored
* pool.
*/
size_t objsize_min;
/** next free factor pool in the cache. */
struct factor_pool *next;
};
typedef rb_tree(struct factor_pool) factor_tree_t;
/** A slab allocator for a wide range of object sizes. */
struct small_alloc {
uint32_t step_pool_objsize_max;
/**
* All slabs in all pools must be of the same order,
* otherwise small_free() has no way to derive from
* pointer its slab and then the pool.
*/
/**
* An array of "stepped" pools, pool->objsize of adjacent
* pools differ by a fixed size (step).
*/
struct mempool step_pools[STEP_POOL_MAX];
/** A cache for nodes in the factor_pools tree. */
struct factor_pool factor_pool_cache[FACTOR_POOL_MAX];
/** First free element in factor_pool_cache. */
struct factor_pool *factor_pool_next;
/**
* A red-black tree with "factored" pools, i.e.
* each pool differs from its neighbor by a factor.
*/
factor_tree_t factor_pools;
struct slab_cache *cache;
/**
* The factor used for factored pools. Must be > 1.
* Is provided during initialization.
*/
float factor;
/** All slabs in all mempools have the same order. */
uint8_t slab_order;
};
/** Initialize a small memory allocator. */
void
small_alloc_create(struct small_alloc *alloc, struct slab_cache *cache,
uint32_t objsize_min, uint32_t objsize_max,
float alloc_factor);
/** Destroy the allocator and all allocated memory. */
void
small_alloc_destroy(struct small_alloc *alloc);
/** Allocate a piece of memory in the small allocator.
*
* @retval NULL the requested size is beyond objsize_max
* or out of memory
*/
void *
smalloc_nothrow(struct small_alloc *alloc, size_t size);
/** Free memory chunk allocated by the small allocator. */
void
smfree(struct small_alloc *alloc, void *ptr);
/**
* @brief Return an unique index associated with a chunk allocated
* by the allocator.
*
* This index space is more dense than the pointers space,
* especially in the least significant bits. This number is
* needed because some types of box's indexes (e.g. BITSET) have
* better performance then they operate on sequential offsets
* (i.e. dense space) instead of memory pointers (sparse space).
*
* The calculation is based on SLAB number and the position of an
* item within it. Current implementation only guarantees that
* adjacent chunks from one SLAB will have consecutive indexes.
* That is, if two chunks were sequentially allocated from one
* chunk they will have sequential ids. If a second chunk was
* allocated from another SLAB thеn the difference between indexes
* may be more than one.
*
* @param ptr pointer to memory allocated in small_alloc
* @return unique index
*/
size_t
small_ptr_compress(struct small_alloc *alloc, void *ptr);
void *
small_ptr_decompress(struct small_alloc *alloc, size_t val);
typedef void (*mempool_stats_cb)(void *cb_ctx,
struct mempool_stats *stats);
void
small_stats(struct small_alloc *alloc,
struct small_stats *totals,
mempool_stats_cb cb, void *cb_ctx);
#ifdef __cplusplus
#include "exception.h"
static inline void *
smalloc(struct small_alloc *alloc, size_t size)
{
void *ptr = smalloc_nothrow(alloc, size);
if (ptr == NULL)
tnt_raise(LoggedError, ER_MEMORY_ISSUE,
size, "small object allocator", "new slab");
return ptr;
}
static inline void *
smalloc0(struct small_alloc *alloc, size_t size)
{
return memset(smalloc(alloc, size), 0, size);
}
} /* extern "C" */
#endif
#endif /* INCLUDES_TARANTOOL_SMALL_SMALL_H */
|