/home/mdboom/Work/builds/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 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 | block_new(size_t size) |
78 | { |
79 | /* Allocate header and block as one unit. |
80 | ab_mem points just past header. */ |
81 | block *b = (block *)PyMem_Malloc(sizeof(block) + size); |
82 | if (!b) Branch (82:9): [True: 0, False: 267k]
|
83 | return NULL; |
84 | b->ab_size = size; |
85 | b->ab_mem = (void *)(b + 1); |
86 | b->ab_next = NULL; |
87 | b->ab_offset = (char *)_Py_ALIGN_UP(b->ab_mem, ALIGNMENT) - |
88 | (char *)(b->ab_mem); |
89 | return b; |
90 | } |
91 | |
92 | static void |
93 | block_free(block *b) { |
94 | while (b) { Branch (94:12): [True: 267k, False: 59.9k]
|
95 | block *next = b->ab_next; |
96 | PyMem_Free(b); |
97 | b = next; |
98 | } |
99 | } |
100 | |
101 | static void * |
102 | block_alloc(block *b, size_t size) |
103 | { |
104 | void *p; |
105 | assert(b); |
106 | size = _Py_SIZE_ROUND_UP(size, ALIGNMENT); |
107 | if (b->ab_offset + size > b->ab_size) { Branch (107:9): [True: 207k, False: 50.4M]
|
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 | block *newbl = block_new( |
113 | size < DEFAULT_BLOCK_SIZE ? Branch (113:25): [True: 207k, False: 68]
|
114 | DEFAULT_BLOCK_SIZE : size68 ); |
115 | if (!newbl) Branch (115:13): [True: 0, False: 207k]
|
116 | return NULL; |
117 | assert(!b->ab_next); |
118 | b->ab_next = newbl; |
119 | b = newbl; |
120 | } |
121 | |
122 | assert(b->ab_offset + size <= b->ab_size); |
123 | p = (void *)(((char *)b->ab_mem) + b->ab_offset); |
124 | b->ab_offset += size; |
125 | return p; |
126 | } |
127 | |
128 | PyArena * |
129 | _PyArena_New(void) |
130 | { |
131 | PyArena* arena = (PyArena *)PyMem_Malloc(sizeof(PyArena)); |
132 | if (!arena) Branch (132:9): [True: 0, False: 59.9k]
|
133 | return (PyArena*)PyErr_NoMemory(); |
134 | |
135 | arena->a_head = block_new(DEFAULT_BLOCK_SIZE); |
136 | arena->a_cur = arena->a_head; |
137 | if (!arena->a_head) { Branch (137:9): [True: 0, False: 59.9k]
|
138 | PyMem_Free((void *)arena); |
139 | return (PyArena*)PyErr_NoMemory(); |
140 | } |
141 | arena->a_objects = PyList_New(0); |
142 | if (!arena->a_objects) { Branch (142:9): [True: 0, False: 59.9k]
|
143 | block_free(arena->a_head); |
144 | PyMem_Free((void *)arena); |
145 | 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 | return arena; |
155 | } |
156 | |
157 | void |
158 | _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 | 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 | Py_DECREF(arena->a_objects); |
177 | PyMem_Free(arena); |
178 | } |
179 | |
180 | void * |
181 | _PyArena_Malloc(PyArena *arena, size_t size) |
182 | { |
183 | void *p = block_alloc(arena->a_cur, size); |
184 | if (!p) Branch (184:9): [True: 0, False: 50.7M]
|
185 | 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 | if (arena->a_cur->ab_next) { Branch (191:9): [True: 207k, False: 50.4M]
|
192 | 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 | return p; |
201 | } |
202 | |
203 | int |
204 | _PyArena_AddPyObject(PyArena *arena, PyObject *obj) |
205 | { |
206 | int r = PyList_Append(arena->a_objects, obj); |
207 | if (r >= 0) { Branch (207:9): [True: 15.3M, False: 0]
|
208 | Py_DECREF(obj); |
209 | } |
210 | return r; |
211 | } |