Skip to content

Commit 1009b50

Browse files
committed
Add per-size freelists. Work in progress
1 parent 155c44b commit 1009b50

24 files changed

+283
-122
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ _Py_eval_breaker_bit_is_set(PyThreadState *tstate, uintptr_t bit)
342342
void _Py_set_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit);
343343
void _Py_unset_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit);
344344

345-
PyAPI_FUNC(_PyStackRef) _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyStackRef right, double value);
345+
PyAPI_FUNC(_PyStackRef) _PyFloat_FromDouble_ConsumeInputs(PyThreadState *ts, _PyStackRef left, _PyStackRef right, double value);
346346

347347
#ifdef __cplusplus
348348
}

Include/internal/pycore_floatobject.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ struct _Py_float_runtime_state {
3131
};
3232

3333

34-
35-
36-
PyAPI_FUNC(void) _PyFloat_ExactDealloc(PyObject *op);
34+
PyAPI_FUNC(void) _PyFloat_ExactDealloc(PyThreadState *ts, PyObject *op);
3735

3836

3937
extern void _PyFloat_DebugMallocStats(FILE* out);

Include/internal/pycore_freelist.h

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ _Py_freelists_GET(void)
3030

3131
// Pushes `op` to the freelist, calls `freefunc` if the freelist is full
3232
#define _Py_FREELIST_FREE(NAME, op, freefunc) \
33-
_PyFreeList_Free(&_Py_freelists_GET()->NAME, _PyObject_CAST(op), \
34-
Py_ ## NAME ## _MAXFREELIST, freefunc)
33+
_PyFreeList_Free(&_Py_freelists_GET()->NAME, _PyObject_CAST(op), freefunc)
3534
// Pushes `op` to the freelist, returns 1 if successful, 0 if the freelist is full
36-
#define _Py_FREELIST_PUSH(NAME, op, limit) \
37-
_PyFreeList_Push(&_Py_freelists_GET()->NAME, _PyObject_CAST(op), limit)
35+
#define _Py_FREELIST_PUSH(NAME, op) \
36+
_PyFreeList_Push(&_Py_freelists_GET()->NAME, _PyObject_CAST(op))
3837

3938
// Pops a PyObject from the freelist, returns NULL if the freelist is empty.
4039
#define _Py_FREELIST_POP(TYPE, NAME) \
@@ -45,26 +44,36 @@ _Py_freelists_GET(void)
4544
#define _Py_FREELIST_POP_MEM(NAME) \
4645
_PyFreeList_PopMem(&_Py_freelists_GET()->NAME)
4746

48-
#define _Py_FREELIST_SIZE(NAME) (int)((_Py_freelists_GET()->NAME).size)
47+
static inline uint32_t
48+
_PyFreeList_Size(struct _Py_freelist *fl)
49+
{
50+
return fl->capacity - fl->available;
51+
}
52+
53+
static inline void
54+
_PyFreeList_Init(struct _Py_freelist *fl, uint32_t capacity)
55+
{
56+
fl->freelist = NULL;
57+
fl->capacity = fl->available = 0; // capacity;
58+
}
4959

5060
static inline int
51-
_PyFreeList_Push(struct _Py_freelist *fl, void *obj, Py_ssize_t maxsize)
61+
_PyFreeList_Push(struct _Py_freelist *fl, void *obj)
5262
{
53-
if (fl->size < maxsize && fl->size >= 0) {
63+
if (fl->available != 0) {
5464
FT_ATOMIC_STORE_PTR_RELAXED(*(void **)obj, fl->freelist);
5565
fl->freelist = obj;
56-
fl->size++;
66+
fl->available--;
5767
OBJECT_STAT_INC(to_freelist);
5868
return 1;
5969
}
6070
return 0;
6171
}
6272

6373
static inline void
64-
_PyFreeList_Free(struct _Py_freelist *fl, void *obj, Py_ssize_t maxsize,
65-
freefunc dofree)
74+
_PyFreeList_Free(struct _Py_freelist *fl, void *obj, freefunc dofree)
6675
{
67-
if (!_PyFreeList_Push(fl, obj, maxsize)) {
76+
if (!_PyFreeList_Push(fl, obj)) {
6877
dofree(obj);
6978
}
7079
}
@@ -74,9 +83,9 @@ _PyFreeList_PopNoStats(struct _Py_freelist *fl)
7483
{
7584
void *obj = fl->freelist;
7685
if (obj != NULL) {
77-
assert(fl->size > 0);
86+
assert(fl->capacity > 0);
7887
fl->freelist = *(void **)obj;
79-
fl->size--;
88+
fl->available++;
8089
}
8190
return obj;
8291
}

Include/internal/pycore_freelist_state.h

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,10 @@ struct _Py_freelist {
3232
// For PyObjects, this overlaps with the `ob_refcnt` field or the `ob_tid`
3333
// field.
3434
void *freelist;
35-
36-
// The number of items in the free list or -1 if the free list is disabled
37-
Py_ssize_t size;
38-
};
39-
40-
struct _Py_freelists {
41-
struct _Py_freelist floats;
42-
struct _Py_freelist ints;
43-
struct _Py_freelist tuples[PyTuple_MAXSAVESIZE];
44-
struct _Py_freelist lists;
45-
struct _Py_freelist list_iters;
46-
struct _Py_freelist tuple_iters;
47-
struct _Py_freelist dicts;
48-
struct _Py_freelist dictkeys;
49-
struct _Py_freelist slices;
50-
struct _Py_freelist contexts;
51-
struct _Py_freelist async_gens;
52-
struct _Py_freelist async_gen_asends;
53-
struct _Py_freelist futureiters;
54-
struct _Py_freelist object_stack_chunks;
55-
struct _Py_freelist unicode_writers;
56-
struct _Py_freelist pymethodobjects;
35+
// The remaining space in this freelist;
36+
uint32_t available;
37+
// The maximum number of items this freelist is allowed to hold
38+
uint32_t capacity;
5739
};
5840

5941
#ifdef __cplusplus

Include/internal/pycore_interp.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ typedef struct _rare_events {
8989

9090
/* interpreter state */
9191

92+
typedef PyObject *(*alloc_func)(PyTypeObject *tp, size_t presize, size_t size);
93+
typedef PyObject *(*free_func)(PyObject *);
94+
9295
/* PyInterpreterState holds the global state for one of the runtime's
9396
interpreters. Typically the initial (main) interpreter is the only one.
9497
@@ -100,6 +103,8 @@ struct _is {
100103
* which is by far the hottest field in this struct
101104
* and should be placed at the beginning. */
102105
struct _ceval_state ceval;
106+
alloc_func alloc;
107+
free_func free;
103108

104109
PyInterpreterState *next;
105110

Include/internal/pycore_object_alloc.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "pycore_object.h" // _PyType_HasFeature()
55
#include "pycore_pystate.h" // _PyThreadState_GET()
66
#include "pycore_tstate.h" // _PyThreadStateImpl
7+
#include "pycore_freelist.h" // _PyFreeList_Pop()
78

89
#ifdef __cplusplus
910
extern "C" {
@@ -65,6 +66,58 @@ _PyObject_ReallocWithType(PyTypeObject *tp, void *ptr, size_t size)
6566
return mem;
6667
}
6768

69+
static inline PyObject *
70+
_PyObject_NewTstate(PyThreadState *ts, PyTypeObject *tp, size_t presize, size_t size)
71+
{
72+
size += presize;
73+
assert(size > 0);
74+
if (size <= SMALL_REQUEST_THRESHOLD) {
75+
int size_cls = (size - 1) >> ALIGNMENT_SHIFT;
76+
struct _Py_freelist *fl = &ts->interp->object_state.freelists.by_size[size_cls];
77+
char *mem = _PyFreeList_Pop(fl);
78+
PyObject *op = (PyObject *)
79+
if (mem != NULL) {
80+
PyObject *op = (PyObject *)(mem + presize);
81+
Py_SET_TYPE(op, tp);
82+
op->ob_refcnt = 1;
83+
return op;
84+
}
85+
}
86+
return ts->interp->alloc(tp, presize, size);
87+
char *mem = PyObject_Malloc(size);
88+
if (mem == NULL) {
89+
return PyErr_NoMemory();
90+
}
91+
PyObject *op = (PyObject *)(PyObject *)(mem + presize);
92+
_PyObject_Init(op, tp);
93+
return op;
94+
}
95+
96+
static inline void
97+
_PyMem_FreeTstate(PyThreadState *ts, PyObject *obj, size_t presize, size_t size)
98+
{
99+
size += presize;
100+
assert(size > 0);
101+
char *mem = ((char *)obj) - presize;
102+
if (size <= SMALL_REQUEST_THRESHOLD) {
103+
int size_cls = (size - 1) >> ALIGNMENT_SHIFT;
104+
struct _Py_freelist *fl = &ts->interp->object_state.freelists.by_size[size_cls];
105+
if (_PyFreeList_Push(fl, mem)) {
106+
return;
107+
}
108+
}
109+
OBJECT_STAT_INC(frees);
110+
ts->interp->free(obj, presize);
111+
_PyRuntime.allocators.standard.obj.free(_PyRuntime.allocators.standard.obj.ctx, mem);
112+
}
113+
114+
static inline void
115+
_PyObject_FreeTstate(PyThreadState *ts, PyObject *obj, size_t size)
116+
{
117+
assert(!PyObject_IS_GC(obj));
118+
_PyMem_FreeTstate(ts, obj, 0, size);
119+
}
120+
68121
#ifdef __cplusplus
69122
}
70123
#endif

Include/internal/pycore_object_state.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
#include "pycore_freelist_state.h" // _Py_freelists
1212
#include "pycore_hashtable.h" // _Py_hashtable_t
13+
#include "pycore_obmalloc.h" // NB_SMALL_SIZE_CLASSES
1314

1415

1516
/* Reference tracer state */
@@ -26,6 +27,26 @@ struct _py_object_runtime_state {
2627
int _not_used;
2728
};
2829

30+
struct _Py_freelists {
31+
struct _Py_freelist by_size[NB_SMALL_SIZE_CLASSES];
32+
struct _Py_freelist floats;
33+
struct _Py_freelist ints;
34+
struct _Py_freelist tuples[PyTuple_MAXSAVESIZE];
35+
struct _Py_freelist lists;
36+
struct _Py_freelist list_iters;
37+
struct _Py_freelist tuple_iters;
38+
struct _Py_freelist dicts;
39+
struct _Py_freelist dictkeys;
40+
struct _Py_freelist slices;
41+
struct _Py_freelist contexts;
42+
struct _Py_freelist async_gens;
43+
struct _Py_freelist async_gen_asends;
44+
struct _Py_freelist futureiters;
45+
struct _Py_freelist object_stack_chunks;
46+
struct _Py_freelist unicode_writers;
47+
struct _Py_freelist pymethodobjects;
48+
};
49+
2950
struct _py_object_state {
3051
#if !defined(Py_GIL_DISABLED)
3152
struct _Py_freelists freelists;

Include/internal/pycore_stackref.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ _PyStackRef_DUP(_PyStackRef ref, const char *filename, int linenumber)
171171
#define PyStackRef_DUP(REF) _PyStackRef_DUP(REF, __FILE__, __LINE__)
172172

173173
extern void PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct);
174+
extern void PyStackRef_CLOSE_SPECIALIZED_TSTATE(PyThreadState *tstate, _PyStackRef ref, destructor_tstate destruct);
174175

175176
static inline _PyStackRef
176177
PyStackRef_MakeHeapSafe(_PyStackRef ref)
@@ -316,6 +317,13 @@ PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct)
316317
PyStackRef_CLOSE(ref);
317318
}
318319

320+
static inline void
321+
PyStackRef_CLOSE_SPECIALIZED_TSTATE(PyThreadState *ts, _PyStackRef ref, destructor_tstate destruct)
322+
{
323+
(void)destruct;
324+
PyStackRef_CLOSE(ref);
325+
}
326+
319327
static inline _PyStackRef
320328
PyStackRef_DUP(_PyStackRef stackref)
321329
{
@@ -575,6 +583,15 @@ PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct)
575583
}
576584
}
577585

586+
static inline void
587+
PyStackRef_CLOSE_SPECIALIZED_TSTATE(PyThreadState *tstate, _PyStackRef ref, destructor_tstate destruct)
588+
{
589+
assert(!PyStackRef_IsNull(ref));
590+
if (PyStackRef_RefcountOnObject(ref)) {
591+
Py_DECREF_MORTAL_SPECIALIZED_TSTATE(tstate, BITS_TO_PTR(ref), destruct);
592+
}
593+
}
594+
578595
#ifdef _WIN32
579596
#define PyStackRef_XCLOSE PyStackRef_CLOSE
580597
#else

Include/internal/pycore_tstate.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ extern "C" {
99
#endif
1010

1111
#include "pycore_brc.h" // struct _brc_thread_state
12-
#include "pycore_freelist_state.h" // struct _Py_freelists
1312
#include "pycore_mimalloc.h" // struct _mimalloc_thread_state
1413
#include "pycore_qsbr.h" // struct qsbr
1514

Include/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ typedef int (*traverseproc)(PyObject *, visitproc, void *);
351351

352352
typedef void (*freefunc)(void *);
353353
typedef void (*destructor)(PyObject *);
354+
typedef void (*destructor_tstate)(struct _ts *, PyObject *);
354355
typedef PyObject *(*getattrfunc)(PyObject *, char *);
355356
typedef PyObject *(*getattrofunc)(PyObject *, PyObject *);
356357
typedef int (*setattrfunc)(PyObject *, char *, PyObject *);

0 commit comments

Comments
 (0)