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