Skip to content

Commit 4b36668

Browse files
committed
Reduce number of branches in freelist push
1 parent f2daa96 commit 4b36668

File tree

6 files changed

+57
-22
lines changed

6 files changed

+57
-22
lines changed

Include/internal/pycore_freelist.h

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ _Py_freelists_GET(void)
3131

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

4040
// Pops a PyObject from the freelist, returns NULL if the freelist is empty.
4141
#define _Py_FREELIST_POP(TYPE, NAME) \
@@ -46,26 +46,39 @@ _Py_freelists_GET(void)
4646
#define _Py_FREELIST_POP_MEM(NAME) \
4747
_PyFreeList_PopMem(&_Py_freelists_GET()->NAME)
4848

49-
#define _Py_FREELIST_SIZE(NAME) (int)((_Py_freelists_GET()->NAME).size)
49+
static inline uint32_t
50+
_PyFreeList_Size(struct _Py_freelist *fl)
51+
{
52+
return fl->capacity - fl->available;
53+
}
54+
55+
#define _Py_FREELIST_SIZE(NAME) _PyFreeList_Size(&_Py_freelists_GET()->NAME)
56+
57+
static inline void
58+
_PyFreeList_Init(struct _Py_freelist *fl, uint32_t capacity)
59+
{
60+
fl->freelist = NULL;
61+
fl->capacity = fl->available = capacity;
62+
}
5063

5164
static inline int
52-
_PyFreeList_Push(struct _Py_freelist *fl, void *obj, Py_ssize_t maxsize)
65+
_PyFreeList_Push(struct _Py_freelist *fl, void *obj)
5366
{
54-
if (fl->size < maxsize && fl->size >= 0) {
67+
if (fl->available != 0) {
5568
FT_ATOMIC_STORE_PTR_RELAXED(*(void **)obj, fl->freelist);
5669
fl->freelist = obj;
57-
fl->size++;
70+
fl->available--;
5871
OBJECT_STAT_INC(to_freelist);
5972
return 1;
6073
}
6174
return 0;
6275
}
6376

6477
static inline void
65-
_PyFreeList_Free(struct _Py_freelist *fl, void *obj, Py_ssize_t maxsize,
78+
_PyFreeList_Free(struct _Py_freelist *fl, void *obj,
6679
freefunc dofree)
6780
{
68-
if (!_PyFreeList_Push(fl, obj, maxsize)) {
81+
if (!_PyFreeList_Push(fl, obj)) {
6982
dofree(obj);
7083
}
7184
}
@@ -75,9 +88,10 @@ _PyFreeList_PopNoStats(struct _Py_freelist *fl)
7588
{
7689
void *obj = fl->freelist;
7790
if (obj != NULL) {
78-
assert(fl->size > 0);
91+
assert(fl->capacity > 0);
7992
fl->freelist = *(void **)obj;
80-
fl->size--;
93+
fl->available++;
94+
assert(fl->available <= fl->capacity);
8195
}
8296
return obj;
8397
}
@@ -97,9 +111,7 @@ static inline void *
97111
_PyFreeList_PopMem(struct _Py_freelist *fl)
98112
{
99113
void *op = _PyFreeList_PopNoStats(fl);
100-
if (op != NULL) {
101-
OBJECT_STAT_INC(from_freelist);
102-
}
114+
OBJECT_STAT_INC_COND(from_freelist, op != NULL);
103115
return op;
104116
}
105117

Include/internal/pycore_freelist_state.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ struct _Py_freelist {
3636
// For PyObjects, this overlaps with the `ob_refcnt` field or the `ob_tid`
3737
// field.
3838
void *freelist;
39-
40-
// The number of items in the free list or -1 if the free list is disabled
41-
Py_ssize_t size;
39+
// The remaining space in this freelist;
40+
uint32_t available;
41+
// The maximum number of items this freelist is allowed to hold
42+
uint32_t capacity;
4243
};
4344

4445
struct _Py_freelists {

Modules/_asynciomodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1826,7 +1826,7 @@ FutureIter_dealloc(PyObject *it)
18261826
PyObject_GC_UnTrack(it);
18271827
tp->tp_clear(it);
18281828

1829-
if (!_Py_FREELIST_PUSH(futureiters, it, Py_futureiters_MAXFREELIST)) {
1829+
if (!_Py_FREELIST_PUSH(futureiters, it)) {
18301830
PyObject_GC_Del(it);
18311831
Py_DECREF(tp);
18321832
}

Objects/object.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -900,10 +900,11 @@ clear_freelist(struct _Py_freelist *freelist, int is_finalization,
900900
while ((ptr = _PyFreeList_PopNoStats(freelist)) != NULL) {
901901
dofree(ptr);
902902
}
903-
assert(freelist->size == 0 || freelist->size == -1);
903+
assert(_PyFreeList_Size(freelist) == 0);
904904
assert(freelist->freelist == NULL);
905+
905906
if (is_finalization) {
906-
freelist->size = -1;
907+
_PyFreeList_Init(freelist, 0);
907908
}
908909
}
909910

Objects/tupleobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,7 @@ maybe_freelist_push(PyTupleObject *op)
11661166
}
11671167
Py_ssize_t index = Py_SIZE(op) - 1;
11681168
if (index < PyTuple_MAXSAVESIZE) {
1169-
return _Py_FREELIST_PUSH(tuples[index], op, Py_tuple_MAXFREELIST);
1169+
return _Py_FREELIST_PUSH(tuples[index], op);
11701170
}
11711171
return 0;
11721172
}

Python/pystate.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,26 @@ free_interpreter(PyInterpreterState *interp)
590590
PyMem_RawFree(interp);
591591
}
592592
}
593+
594+
static void
595+
init_freelists(struct _Py_freelists *freelists)
596+
{
597+
_PyFreeList_Init(&freelists->floats, Py_floats_MAXFREELIST);
598+
for (Py_ssize_t i = 0; i < PyTuple_MAXSAVESIZE; i++) {
599+
_PyFreeList_Init(&freelists->tuples[i], Py_tuple_MAXFREELIST);
600+
}
601+
_PyFreeList_Init(&freelists->lists, Py_lists_MAXFREELIST);
602+
_PyFreeList_Init(&freelists->list_iters, Py_list_iters_MAXFREELIST);
603+
_PyFreeList_Init(&freelists->tuple_iters, Py_tuple_iters_MAXFREELIST);
604+
_PyFreeList_Init(&freelists->dicts, Py_dicts_MAXFREELIST);
605+
_PyFreeList_Init(&freelists->dictkeys, Py_dictkeys_MAXFREELIST);
606+
_PyFreeList_Init(&freelists->slices, Py_slices_MAXFREELIST);
607+
_PyFreeList_Init(&freelists->contexts, Py_contexts_MAXFREELIST);
608+
_PyFreeList_Init(&freelists->async_gens, Py_async_gens_MAXFREELIST);
609+
_PyFreeList_Init(&freelists->async_gen_asends, Py_async_gen_asends_MAXFREELIST);
610+
_PyFreeList_Init(&freelists->futureiters, Py_futureiters_MAXFREELIST);
611+
}
612+
593613
#ifndef NDEBUG
594614
static inline int check_interpreter_whence(long);
595615
#endif
@@ -699,6 +719,7 @@ init_interpreter(PyInterpreterState *interp,
699719
_Py_stackref_associate(interp, Py_True, PyStackRef_True);
700720
#endif
701721

722+
init_freelists(&interp->object_state.freelists);
702723
interp->_initialized = 1;
703724
return _PyStatus_OK();
704725
}

0 commit comments

Comments
 (0)