Skip to content

Commit 00a5bc6

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

22 files changed

+206
-113
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_object_alloc.h

Lines changed: 38 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,43 @@ _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 size)
71+
{
72+
assert(size > 0);
73+
if (size <= SMALL_REQUEST_THRESHOLD) {
74+
int size_cls = (size - 1) >> ALIGNMENT_SHIFT;
75+
struct _Py_freelist *fl = &ts->interp->object_state.freelists.by_size[size_cls];
76+
PyObject *op = (PyObject *)_PyFreeList_Pop(fl);
77+
if (op != NULL) {
78+
Py_SET_TYPE(op, tp);
79+
op->ob_refcnt = 1;
80+
return op;
81+
}
82+
}
83+
PyObject *op = (PyObject *) PyObject_Malloc(size);
84+
if (op == NULL) {
85+
return PyErr_NoMemory();
86+
}
87+
_PyObject_Init(op, tp);
88+
return op;
89+
}
90+
91+
static inline void
92+
_PyObject_FreeTstate(PyThreadState *ts, PyObject *obj, size_t size)
93+
{
94+
assert(size > 0);
95+
if (size <= SMALL_REQUEST_THRESHOLD) {
96+
int size_cls = (size - 1) >> ALIGNMENT_SHIFT;
97+
struct _Py_freelist *fl = &ts->interp->object_state.freelists.by_size[size_cls];
98+
if (_PyFreeList_Push(fl, obj)) {
99+
return;
100+
}
101+
}
102+
PyObject_Free(obj);
103+
}
104+
105+
68106
#ifdef __cplusplus
69107
}
70108
#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 *);

Include/refcount.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,23 @@ static inline void _Py_DECREF_MORTAL_SPECIALIZED(const char *filename, int linen
420420
}
421421
#define Py_DECREF_MORTAL_SPECIALIZED(op, destruct) _Py_DECREF_MORTAL_SPECIALIZED(__FILE__, __LINE__, op, destruct)
422422

423+
424+
static inline void _Py_DECREF_MORTAL_SPECIALIZED_TSTATE(const char *filename, int lineno, PyThreadState *tstate, PyObject *op, destructor_tstate destruct)
425+
{
426+
if (op->ob_refcnt <= 0) {
427+
_Py_NegativeRefcount(filename, lineno, op);
428+
}
429+
_Py_DECREF_STAT_INC();
430+
assert(!_Py_IsStaticImmortal(op));
431+
if (!_Py_IsImmortal(op)) {
432+
_Py_DECREF_DecRefTotal();
433+
}
434+
if (--op->ob_refcnt == 0) {
435+
destruct(tstate, op);
436+
}
437+
}
438+
#define Py_DECREF_MORTAL_SPECIALIZED_TSTATE(ts, op, destruct) _Py_DECREF_MORTAL_SPECIALIZED_TSTATE(__FILE__, __LINE__, ts, op, destruct)
439+
423440
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
424441
{
425442
#if SIZEOF_VOID_P > 4
@@ -464,6 +481,16 @@ static inline void Py_DECREF_MORTAL_SPECIALIZED(PyObject *op, destructor destruc
464481
}
465482
#define Py_DECREF_MORTAL_SPECIALIZED(op, destruct) Py_DECREF_MORTAL_SPECIALIZED(_PyObject_CAST(op), destruct)
466483

484+
static inline void Py_DECREF_MORTAL_SPECIALIZED_TSTATE(PyThreadState *tstate, PyObject *op, destructor_tstate destruct)
485+
{
486+
assert(!_Py_IsStaticImmortal(op));
487+
_Py_DECREF_STAT_INC();
488+
if (--op->ob_refcnt == 0) {
489+
destruct(tstate, op);
490+
}
491+
}
492+
#define Py_DECREF_MORTAL_SPECIALIZED_TSTATE(op, destruct) Py_DECREF_MORTAL_SPECIALIZED_TSTATE(_PyObject_CAST(op), destruct)
493+
467494
static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
468495
{
469496
// Non-limited C API and limited C API for Python 3.9 and older access

0 commit comments

Comments
 (0)