Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 77f1bb2

Browse files
committed
Real arena implementation
Replace the toy arena implementation with a real one, based on allocating 8K chunks of memory by default.
1 parent 77e42ff commit 77f1bb2

2 files changed

Lines changed: 84 additions & 72 deletions

File tree

Include/pyarena.h

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,10 @@ extern "C" {
2323

2424
PyAPI_FUNC(void *) PyArena_Malloc(PyArena *, size_t);
2525

26-
/* The next two routines aren't proper arena allocation routines.
27-
They exist to experiment with the arena API without making wholesale
28-
changes to the implementation.
29-
30-
The two functions register pointers with the arena id. These
31-
are externally allocated pointers that will be freed when the
32-
arena is freed. One takes a pointer allocated with malloc. The
33-
other takes a PyObject that is DECREFed when the arena is freed.
34-
*/
35-
PyAPI_FUNC(int) PyArena_AddMallocPointer(PyArena *, void *);
26+
/* This routines isn't a proper arena allocation routine. It takes
27+
a PyObject* and records it so that it can be DECREFed when the
28+
arena is freed.
29+
*/
3630
PyAPI_FUNC(int) PyArena_AddPyObject(PyArena *, PyObject *);
3731

3832
#ifdef __cplusplus

Python/pyarena.c

Lines changed: 80 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
#include "Python.h"
22
#include "pyarena.h"
33

4-
/* An arena list is a linked list that can store either pointers or
5-
PyObjects. The type is clear from context.
6-
*/
4+
/* An arena list is a linked list that can store PyObjects. */
75

86
typedef struct _arena_list {
9-
struct _arena_list *al_next;
10-
void *al_pointer;
7+
struct _arena_list *al_next;
8+
void *al_pointer;
119
} PyArenaList;
1210

13-
/* There are two linked lists in an arena, one for malloc pointers and
14-
one for PyObject. For each list, there is a pointer to the head
15-
and to the tail. The head is used to free the list. The tail is
16-
used to add a new element to the list.
11+
/* A simple arena block structure */
12+
/* TODO(jhylton): Measurement to justify block size. */
1713

18-
The list always keeps one un-used node at the end of the list.
19-
*/
14+
#define DEFAULT_BLOCK_SIZE 8192
15+
typedef struct _block {
16+
size_t ab_size;
17+
size_t ab_offset;
18+
struct _block *ab_next;
19+
void *ab_mem;
20+
} block;
2021

2122
struct _arena {
22-
PyArenaList *a_malloc_head;
23-
PyArenaList *a_malloc_tail;
24-
PyArenaList *a_object_head;
25-
PyArenaList *a_object_tail;
23+
block *a_head;
24+
block *a_cur;
25+
PyArenaList *a_object_head;
26+
PyArenaList *a_object_tail;
2627
};
2728

2829
static PyArenaList*
@@ -40,31 +41,62 @@ PyArenaList_New(void)
4041
static void
4142
PyArenaList_FreeObject(PyArenaList *alist)
4243
{
43-
while (alist) {
44-
PyArenaList *prev;
45-
Py_XDECREF((PyObject *)alist->al_pointer);
46-
alist->al_pointer = NULL;
47-
prev = alist;
48-
alist = alist->al_next;
49-
free(prev);
50-
}
44+
while (alist) {
45+
PyArenaList *prev;
46+
Py_XDECREF((PyObject *)alist->al_pointer);
47+
alist->al_pointer = NULL;
48+
prev = alist;
49+
alist = alist->al_next;
50+
free(prev);
51+
}
5152
}
5253

53-
static void
54-
PyArenaList_FreeMalloc(PyArenaList *alist)
54+
static block *
55+
block_new(size_t size)
5556
{
56-
while (alist) {
57-
PyArenaList *prev;
58-
if (alist->al_pointer) {
59-
free(alist->al_pointer);
57+
/* Allocate header and block as one unit. ab_mem points just past header. */
58+
block *b = (block *)malloc(sizeof(block) + size);
59+
if (!b)
60+
return NULL;
61+
b->ab_size = size;
62+
b->ab_mem = (void *)(b + 1);
63+
b->ab_next = NULL;
64+
b->ab_offset = 0;
65+
return b;
66+
}
67+
68+
static void
69+
block_free(block *b) {
70+
while (b) {
71+
block *next = b->ab_next;
72+
free(b);
73+
b = next;
6074
}
61-
alist->al_pointer = NULL;
62-
prev = alist;
63-
alist = alist->al_next;
64-
free(prev);
65-
}
6675
}
6776

77+
static void *
78+
block_alloc(block *b, size_t size)
79+
{
80+
void *p;
81+
assert(b);
82+
if (b->ab_offset + size > b->ab_size) {
83+
/* If we need to allocate more memory than will fit in the default
84+
block, allocate a one-off block that is exactly the right size. */
85+
/* TODO(jhylton): Think more about space waste at end of block */
86+
block *new = block_new(
87+
size < DEFAULT_BLOCK_SIZE ? DEFAULT_BLOCK_SIZE : size);
88+
if (!new)
89+
return NULL;
90+
assert(!b->ab_next);
91+
b->ab_next = new;
92+
b = new;
93+
}
94+
95+
assert(b->ab_offset + size <= b->ab_size);
96+
p = (void *)(((char *)b->ab_mem) + b->ab_offset);
97+
b->ab_offset += size;
98+
return p;
99+
}
68100

69101
PyArena *
70102
PyArena_New()
@@ -73,47 +105,33 @@ PyArena_New()
73105
if (!arena)
74106
return NULL;
75107

108+
arena->a_head = block_new(DEFAULT_BLOCK_SIZE);
109+
arena->a_cur = arena->a_head;
76110
arena->a_object_head = PyArenaList_New();
77111
arena->a_object_tail = arena->a_object_head;
78-
arena->a_malloc_head = PyArenaList_New();
79-
arena->a_malloc_tail = arena->a_malloc_head;
80112
return arena;
81113
}
82114

83115
void
84116
PyArena_Free(PyArena *arena)
85117
{
86-
assert(arena);
87-
PyArenaList_FreeObject(arena->a_object_head);
88-
PyArenaList_FreeMalloc(arena->a_malloc_head);
89-
free(arena);
118+
assert(arena);
119+
block_free(arena->a_head);
120+
PyArenaList_FreeObject(arena->a_object_head);
121+
free(arena);
90122
}
91123

92124
void *
93125
PyArena_Malloc(PyArena *arena, size_t size)
94126
{
95-
/* A better implementation might actually use an arena. The current
96-
approach is just a trivial implementation of the API that allows
97-
it to be tested.
98-
*/
99-
void *p;
100-
assert(size != 0);
101-
p = malloc(size);
102-
if (p)
103-
PyArena_AddMallocPointer(arena, p);
104-
return p;
105-
}
106-
107-
int
108-
PyArena_AddMallocPointer(PyArena *arena, void *pointer)
109-
{
110-
PyArenaList *tail = arena->a_malloc_tail;
111-
assert(pointer);
112-
assert(tail->al_pointer != pointer);
113-
tail->al_next = PyArenaList_New();
114-
tail->al_pointer = pointer;
115-
arena->a_malloc_tail = tail->al_next;
116-
return 1;
127+
void *p = block_alloc(arena->a_cur, size);
128+
if (!p)
129+
return NULL;
130+
/* Reset cur if we allocated a new block. */
131+
if (arena->a_cur->ab_next) {
132+
arena->a_cur = arena->a_cur->ab_next;
133+
}
134+
return p;
117135
}
118136

119137
int

0 commit comments

Comments
 (0)