LCOV - code coverage report
Current view: | top level - Python - pyarena.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 50 | 59 | 84.7 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 7 | 7 | 100.0 %
|  |  | Branches: | 13 | 20 | 65.0 %

               Branch data     Line data    Source code
           1                 :            : #include "Python.h"
           2                 :            : #include "pycore_pyarena.h"       // PyArena
           3                 :            :
           4                 :            : /* A simple arena block structure.
           5                 :            :
           6                 :            :    Measurements with standard library modules suggest the average
           7                 :            :    allocation is about 20 bytes and that most compiles use a single
           8                 :            :    block.
           9                 :            :
          10                 :            :    TODO(jhylton): Think about a realloc API, maybe just for the last
          11                 :            :    allocation?
          12                 :            : */
          13                 :            :
          14                 :            : #define DEFAULT_BLOCK_SIZE 8192
          15                 :            : #define ALIGNMENT               8
          16                 :            :
          17                 :            : typedef struct _block {
          18                 :            :     /* Total number of bytes owned by this block available to pass out.
          19                 :            :      * Read-only after initialization.  The first such byte starts at
          20                 :            :      * ab_mem.
          21                 :            :      */
          22                 :            :     size_t ab_size;
          23                 :            :
          24                 :            :     /* Total number of bytes already passed out.  The next byte available
          25                 :            :      * to pass out starts at ab_mem + ab_offset.
          26                 :            :      */
          27                 :            :     size_t ab_offset;
          28                 :            :
          29                 :            :     /* An arena maintains a singly-linked, NULL-terminated list of
          30                 :            :      * all blocks owned by the arena.  These are linked via the
          31                 :            :      * ab_next member.
          32                 :            :      */
          33                 :            :     struct _block *ab_next;
          34                 :            :
          35                 :            :     /* Pointer to the first allocatable byte owned by this block.  Read-
          36                 :            :      * only after initialization.
          37                 :            :      */
          38                 :            :     void *ab_mem;
          39                 :            : } block;
          40                 :            :
          41                 :            : /* The arena manages two kinds of memory, blocks of raw memory
          42                 :            :    and a list of PyObject* pointers.  PyObjects are decrefed
          43                 :            :    when the arena is freed.
          44                 :            : */
          45                 :            :
          46                 :            : struct _arena {
          47                 :            :     /* Pointer to the first block allocated for the arena, never NULL.
          48                 :            :        It is used only to find the first block when the arena is
          49                 :            :        being freed.
          50                 :            :      */
          51                 :            :     block *a_head;
          52                 :            :
          53                 :            :     /* Pointer to the block currently used for allocation.  Its
          54                 :            :        ab_next field should be NULL.  If it is not-null after a
          55                 :            :        call to block_alloc(), it means a new block has been allocated
          56                 :            :        and a_cur should be reset to point it.
          57                 :            :      */
          58                 :            :     block *a_cur;
          59                 :            :
          60                 :            :     /* A Python list object containing references to all the PyObject
          61                 :            :        pointers associated with this arena.  They will be DECREFed
          62                 :            :        when the arena is freed.
          63                 :            :     */
          64                 :            :     PyObject *a_objects;
          65                 :            :
          66                 :            : #if defined(Py_DEBUG)
          67                 :            :     /* Debug output */
          68                 :            :     size_t total_allocs;
          69                 :            :     size_t total_size;
          70                 :            :     size_t total_blocks;
          71                 :            :     size_t total_block_size;
          72                 :            :     size_t total_big_blocks;
          73                 :            : #endif
          74                 :            : };
          75                 :            :
          76                 :            : static block *
          77                 :      42404 : block_new(size_t size)
          78                 :            : {
          79                 :            :     /* Allocate header and block as one unit.
          80                 :            :        ab_mem points just past header. */
          81                 :      42404 :     block *b = (block *)PyMem_Malloc(sizeof(block) + size);
          82         [ -  + ]:      42404 :     if (!b)
          83                 :          0 :         return NULL;
          84                 :      42404 :     b->ab_size = size;
          85                 :      42404 :     b->ab_mem = (void *)(b + 1);
          86                 :      42404 :     b->ab_next = NULL;
          87                 :      42404 :     b->ab_offset = (char *)_Py_ALIGN_UP(b->ab_mem, ALIGNMENT) -
          88                 :      42404 :             (char *)(b->ab_mem);
          89                 :      42404 :     return b;
          90                 :            : }
          91                 :            :
          92                 :            : static void
          93                 :        351 : block_free(block *b) {
          94         [ +  + ]:      42755 :     while (b) {
          95                 :      42404 :         block *next = b->ab_next;
          96                 :      42404 :         PyMem_Free(b);
          97                 :      42404 :         b = next;
          98                 :            :     }
          99                 :        351 : }
         100                 :            :
         101                 :            : static void *
         102                 :    9941956 : block_alloc(block *b, size_t size)
         103                 :            : {
         104                 :            :     void *p;
         105                 :            :     assert(b);
         106                 :    9941956 :     size = _Py_SIZE_ROUND_UP(size, ALIGNMENT);
         107         [ +  + ]:    9941956 :     if (b->ab_offset + size > b->ab_size) {
         108                 :            :         /* If we need to allocate more memory than will fit in
         109                 :            :            the default block, allocate a one-off block that is
         110                 :            :            exactly the right size. */
         111                 :            :         /* TODO(jhylton): Think about space waste at end of block */
         112                 :      42053 :         block *newbl = block_new(
         113                 :            :                         size < DEFAULT_BLOCK_SIZE ?
         114                 :            :                         DEFAULT_BLOCK_SIZE : size);
         115         [ -  + ]:      42053 :         if (!newbl)
         116                 :          0 :             return NULL;
         117                 :            :         assert(!b->ab_next);
         118                 :      42053 :         b->ab_next = newbl;
         119                 :      42053 :         b = newbl;
         120                 :            :     }
         121                 :            :
         122                 :            :     assert(b->ab_offset + size <= b->ab_size);
         123                 :    9941956 :     p = (void *)(((char *)b->ab_mem) + b->ab_offset);
         124                 :    9941956 :     b->ab_offset += size;
         125                 :    9941956 :     return p;
         126                 :            : }
         127                 :            :
         128                 :            : PyArena *
         129                 :        351 : _PyArena_New(void)
         130                 :            : {
         131                 :        351 :     PyArena* arena = (PyArena *)PyMem_Malloc(sizeof(PyArena));
         132         [ -  + ]:        351 :     if (!arena)
         133                 :          0 :         return (PyArena*)PyErr_NoMemory();
         134                 :            :
         135                 :        351 :     arena->a_head = block_new(DEFAULT_BLOCK_SIZE);
         136                 :        351 :     arena->a_cur = arena->a_head;
         137         [ -  + ]:        351 :     if (!arena->a_head) {
         138                 :          0 :         PyMem_Free((void *)arena);
         139                 :          0 :         return (PyArena*)PyErr_NoMemory();
         140                 :            :     }
         141                 :        351 :     arena->a_objects = PyList_New(0);
         142         [ -  + ]:        351 :     if (!arena->a_objects) {
         143                 :          0 :         block_free(arena->a_head);
         144                 :          0 :         PyMem_Free((void *)arena);
         145                 :          0 :         return (PyArena*)PyErr_NoMemory();
         146                 :            :     }
         147                 :            : #if defined(Py_DEBUG)
         148                 :            :     arena->total_allocs = 0;
         149                 :            :     arena->total_size = 0;
         150                 :            :     arena->total_blocks = 1;
         151                 :            :     arena->total_block_size = DEFAULT_BLOCK_SIZE;
         152                 :            :     arena->total_big_blocks = 0;
         153                 :            : #endif
         154                 :        351 :     return arena;
         155                 :            : }
         156                 :            :
         157                 :            : void
         158                 :        351 : _PyArena_Free(PyArena *arena)
         159                 :            : {
         160                 :            :     assert(arena);
         161                 :            : #if defined(Py_DEBUG)
         162                 :            :     /*
         163                 :            :     fprintf(stderr,
         164                 :            :         "alloc=%zu size=%zu blocks=%zu block_size=%zu big=%zu objects=%zu\n",
         165                 :            :         arena->total_allocs, arena->total_size, arena->total_blocks,
         166                 :            :         arena->total_block_size, arena->total_big_blocks,
         167                 :            :         PyList_Size(arena->a_objects));
         168                 :            :     */
         169                 :            : #endif
         170                 :        351 :     block_free(arena->a_head);
         171                 :            :     /* This property normally holds, except when the code being compiled
         172                 :            :        is sys.getobjects(0), in which case there will be two references.
         173                 :            :     assert(arena->a_objects->ob_refcnt == 1);
         174                 :            :     */
         175                 :            :
         176                 :        351 :     Py_DECREF(arena->a_objects);
         177                 :        351 :     PyMem_Free(arena);
         178                 :        351 : }
         179                 :            :
         180                 :            : void *
         181                 :    9941956 : _PyArena_Malloc(PyArena *arena, size_t size)
         182                 :            : {
         183                 :    9941956 :     void *p = block_alloc(arena->a_cur, size);
         184         [ -  + ]:    9941956 :     if (!p)
         185                 :          0 :         return PyErr_NoMemory();
         186                 :            : #if defined(Py_DEBUG)
         187                 :            :     arena->total_allocs++;
         188                 :            :     arena->total_size += size;
         189                 :            : #endif
         190                 :            :     /* Reset cur if we allocated a new block. */
         191         [ +  + ]:    9941956 :     if (arena->a_cur->ab_next) {
         192                 :      42053 :         arena->a_cur = arena->a_cur->ab_next;
         193                 :            : #if defined(Py_DEBUG)
         194                 :            :         arena->total_blocks++;
         195                 :            :         arena->total_block_size += arena->a_cur->ab_size;
         196                 :            :         if (arena->a_cur->ab_size > DEFAULT_BLOCK_SIZE)
         197                 :            :             ++arena->total_big_blocks;
         198                 :            : #endif
         199                 :            :     }
         200                 :    9941956 :     return p;
         201                 :            : }
         202                 :            :
         203                 :            : int
         204                 :    2761788 : _PyArena_AddPyObject(PyArena *arena, PyObject *obj)
         205                 :            : {
         206                 :    2761788 :     int r = PyList_Append(arena->a_objects, obj);
         207         [ +  - ]:    2761788 :     if (r >= 0) {
         208                 :    2761788 :         Py_DECREF(obj);
         209                 :            :     }
         210                 :    2761788 :     return r;
         211                 :            : }

Generated by: LCOV version 1.14