Skip to content

Commit 9dfee07

Browse files
Consolidate the freelist code.
1 parent 3264e8d commit 9dfee07

File tree

2 files changed

+143
-88
lines changed

2 files changed

+143
-88
lines changed

Include/internal/pycore_tuple.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,16 @@ extern void _PyTuple_Fini(PyInterpreterState *);
3838

3939
struct _Py_tuple_state {
4040
#if PyTuple_MAXSAVESIZE > 0
41-
/* Each entry up to PyTuple_MAXSAVESIZE is a free list.
42-
The empty tuple is handled separately, hence declaring one fewer. */
43-
PyTupleObject *free_list[PyTuple_MAXSAVESIZE - 1];
44-
int numfree[PyTuple_MAXSAVESIZE - 1];
41+
/* There is one freelist for each size from 1 to PyTuple_MAXSAVESIZE.
42+
The empty tuple is handled separately.
43+
44+
Each tuple stored in the array is the head of the linked list
45+
(and the next available tuple) for that size. The actual tuple
46+
object is used as the linked list node, with its first item
47+
(ob_item[0]) pointing to the next node (i.e. the previous head).
48+
Each linked list is initially NULL. */
49+
PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
50+
int numfree[PyTuple_MAXSAVESIZE];
4551
#endif
4652
};
4753

Objects/tupleobject.c

Lines changed: 133 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,10 @@ class tuple "PyTupleObject *" "&PyTuple_Type"
1616
#include "clinic/tupleobject.c.h"
1717

1818

19-
#define STATE (interp->tuple)
19+
static inline PyTupleObject * maybe_freelist_pop(Py_ssize_t);
20+
static inline int maybe_freelist_push(PyTupleObject *);
2021

2122

22-
/* Print summary info about the state of the optimized allocator */
23-
void
24-
_PyTuple_DebugMallocStats(FILE *out)
25-
{
26-
#if PyTuple_MAXSAVESIZE > 0
27-
PyInterpreterState *interp = _PyInterpreterState_GET();
28-
for (int i = 1; i < PyTuple_MAXSAVESIZE; i++) {
29-
char buf[128];
30-
PyOS_snprintf(buf, sizeof(buf),
31-
"free %d-sized PyTupleObject", i);
32-
_PyDebugAllocatorStats(out, buf, STATE.numfree[i-1],
33-
_PyObject_VAR_SIZE(&PyTuple_Type, i));
34-
}
35-
#endif
36-
}
37-
3823
/* Allocate an uninitialized tuple object. Before making it public, following
3924
steps must be done:
4025
@@ -48,33 +33,14 @@ _PyTuple_DebugMallocStats(FILE *out)
4833
static PyTupleObject *
4934
tuple_alloc(Py_ssize_t size)
5035
{
51-
PyTupleObject *op;
52-
/* The empty tuple is statically allocated. */
53-
if (size <= 0) {
36+
assert(size != 0); // The empty tuple is statically allocated.
37+
if (size < 0) {
5438
PyErr_BadInternalCall();
5539
return NULL;
5640
}
5741

58-
// Check for max save size > 1.
59-
#if PyTuple_MAXSAVESIZE > 1
60-
PyInterpreterState *interp = _PyInterpreterState_GET();
61-
#ifdef Py_DEBUG
62-
// tuple_alloc() must not be called after _PyTuple_Fini()
63-
assert(STATE.numfree[0] >= 0);
64-
#endif
65-
if (size < PyTuple_MAXSAVESIZE && (op = STATE.free_list[size-1]) != NULL) {
66-
STATE.free_list[size-1] = (PyTupleObject *) op->ob_item[0];
67-
STATE.numfree[size-1]--;
68-
/* Inlined _PyObject_InitVar() without _PyType_HasFeature() test */
69-
#ifdef Py_TRACE_REFS
70-
Py_SET_SIZE(op, size);
71-
Py_SET_TYPE(op, &PyTuple_Type);
72-
#endif
73-
_Py_NewReference((PyObject *)op);
74-
}
75-
else
76-
#endif
77-
{
42+
PyTupleObject *op = maybe_freelist_pop(size);
43+
if (op == NULL) {
7844
/* Check for overflow */
7945
if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - (sizeof(PyTupleObject) -
8046
sizeof(PyObject *))) / sizeof(PyObject *)) {
@@ -226,32 +192,14 @@ tupledealloc(PyTupleObject *op)
226192
PyObject_GC_UnTrack(op);
227193
Py_TRASHCAN_BEGIN(op, tupledealloc)
228194

229-
Py_ssize_t len = Py_SIZE(op);
230-
Py_ssize_t i = len;
195+
Py_ssize_t i = Py_SIZE(op);
231196
while (--i >= 0) {
232197
Py_XDECREF(op->ob_item[i]);
233198
}
234-
#if PyTuple_MAXSAVESIZE > 0
235-
PyInterpreterState *interp = _PyInterpreterState_GET();
236-
#ifdef Py_DEBUG
237-
// tupledealloc() must not be called after _PyTuple_Fini()
238-
assert(STATE.numfree[0] >= 0);
239-
#endif
240-
if (len < (PyTuple_MAXSAVESIZE - 1)
241-
&& STATE.numfree[len-1] < PyTuple_MAXFREELIST
242-
&& Py_IS_TYPE(op, &PyTuple_Type))
243-
{
244-
op->ob_item[0] = (PyObject *) STATE.free_list[len-1];
245-
STATE.numfree[len-1]++;
246-
STATE.free_list[len-1] = op;
247-
goto done; /* return */
199+
if (!maybe_freelist_push(op)) {
200+
Py_TYPE(op)->tp_free((PyObject *)op);
248201
}
249-
#endif
250-
Py_TYPE(op)->tp_free((PyObject *)op);
251202

252-
#if PyTuple_MAXSAVESIZE > 0
253-
done:
254-
#endif
255203
Py_TRASHCAN_END
256204
}
257205

@@ -1014,23 +962,6 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
1014962
return 0;
1015963
}
1016964

1017-
void
1018-
_PyTuple_ClearFreeList(PyInterpreterState *interp)
1019-
{
1020-
#if PyTuple_MAXSAVESIZE > 0
1021-
for (Py_ssize_t i = 1; i < PyTuple_MAXSAVESIZE; i++) {
1022-
PyTupleObject *p = STATE.free_list[i-1];
1023-
STATE.free_list[i-1] = NULL;
1024-
STATE.numfree[i-1] = 0;
1025-
while (p) {
1026-
PyTupleObject *q = p;
1027-
p = (PyTupleObject *)(p->ob_item[0]);
1028-
PyObject_GC_Del(q);
1029-
}
1030-
}
1031-
#endif
1032-
}
1033-
1034965

1035966
PyStatus
1036967
_PyTuple_InitTypes(PyInterpreterState *interp)
@@ -1050,15 +981,20 @@ _PyTuple_InitTypes(PyInterpreterState *interp)
1050981
return _PyStatus_OK();
1051982
}
1052983

984+
static void maybe_freelist_clear(PyInterpreterState *);
985+
static void maybe_freelist_fini(PyInterpreterState *);
986+
1053987
void
1054988
_PyTuple_Fini(PyInterpreterState *interp)
1055989
{
1056-
#if PyTuple_MAXSAVESIZE > 0
1057-
_PyTuple_ClearFreeList(interp);
1058-
for (Py_ssize_t i = 1; i < PyTuple_MAXSAVESIZE; i++) {
1059-
interp->tuple.numfree[i-1] = -1;
1060-
}
1061-
#endif
990+
maybe_freelist_clear(interp);
991+
maybe_freelist_fini(interp);
992+
}
993+
994+
void
995+
_PyTuple_ClearFreeList(PyInterpreterState *interp)
996+
{
997+
maybe_freelist_clear(interp);
1062998
}
1063999

10641000
/*********************** Tuple Iterator **************************/
@@ -1206,3 +1142,116 @@ tuple_iter(PyObject *seq)
12061142
_PyObject_GC_TRACK(it);
12071143
return (PyObject *)it;
12081144
}
1145+
1146+
1147+
/*************
1148+
* freelists *
1149+
*************/
1150+
1151+
#define STATE (interp->tuple)
1152+
#define FREELIST_FINALIZED (STATE.numfree[0] < 0)
1153+
1154+
static inline PyTupleObject *
1155+
maybe_freelist_pop(Py_ssize_t size)
1156+
{
1157+
#if PyTuple_MAXSAVESIZE > 0
1158+
PyInterpreterState *interp = _PyInterpreterState_GET();
1159+
#ifdef Py_DEBUG
1160+
/* maybe_freelist_pop() must not be called after maybe_freelist_fini(). */
1161+
assert(!FREELIST_FINALIZED);
1162+
#endif
1163+
Py_ssize_t index = size - 1;
1164+
if (index < PyTuple_MAXSAVESIZE) {
1165+
PyTupleObject *op = STATE.free_list[index];
1166+
if (op != NULL) {
1167+
/* op is the head of a linked list, with the first item
1168+
pointing to the next node. Here we pop off the old head. */
1169+
STATE.free_list[index] = (PyTupleObject *) op->ob_item[0];
1170+
STATE.numfree[index]--;
1171+
/* Inlined _PyObject_InitVar() without _PyType_HasFeature() test */
1172+
#ifdef Py_TRACE_REFS
1173+
/* maybe_freelist_push() ensures these were already set. */
1174+
// XXX Can we drop these? See commit 68055ce6fe01 (GvR, Dec 1998).
1175+
Py_SET_SIZE(op, size);
1176+
Py_SET_TYPE(op, &PyTuple_Type);
1177+
#endif
1178+
_Py_NewReference((PyObject *)op);
1179+
/* END inlined _PyObject_InitVar() */
1180+
return op;
1181+
}
1182+
}
1183+
#endif
1184+
return NULL;
1185+
}
1186+
1187+
static inline int
1188+
maybe_freelist_push(PyTupleObject *op)
1189+
{
1190+
#if PyTuple_MAXSAVESIZE > 0
1191+
PyInterpreterState *interp = _PyInterpreterState_GET();
1192+
#ifdef Py_DEBUG
1193+
/* maybe_freelist_push() must not be called after maybe_freelist_fini(). */
1194+
assert(!FREELIST_FINALIZED);
1195+
#endif
1196+
Py_ssize_t index = Py_SIZE(op) - 1;
1197+
if (index < PyTuple_MAXSAVESIZE
1198+
&& STATE.numfree[index] < PyTuple_MAXFREELIST
1199+
&& Py_IS_TYPE(op, &PyTuple_Type))
1200+
{
1201+
/* op is the head of a linked list, with the first item
1202+
pointing to the next node. Here we set op as the new head. */
1203+
op->ob_item[0] = (PyObject *) STATE.free_list[index];
1204+
STATE.free_list[index] = op;
1205+
STATE.numfree[index]++;
1206+
return 1;
1207+
}
1208+
#endif
1209+
return 0;
1210+
}
1211+
1212+
static void
1213+
maybe_freelist_clear(PyInterpreterState *interp)
1214+
{
1215+
#if PyTuple_MAXSAVESIZE > 0
1216+
for (Py_ssize_t i = 0; i < PyTuple_MAXSAVESIZE; i++) {
1217+
PyTupleObject *p = STATE.free_list[i];
1218+
STATE.free_list[i] = NULL;
1219+
STATE.numfree[i] = 0;
1220+
while (p) {
1221+
PyTupleObject *q = p;
1222+
p = (PyTupleObject *)(p->ob_item[0]);
1223+
PyObject_GC_Del(q);
1224+
}
1225+
}
1226+
#endif
1227+
}
1228+
1229+
static void
1230+
maybe_freelist_fini(PyInterpreterState *interp)
1231+
{
1232+
#if PyTuple_MAXSAVESIZE > 0
1233+
for (Py_ssize_t i = 0; i < PyTuple_MAXSAVESIZE; i++) {
1234+
STATE.numfree[i] = -1;
1235+
}
1236+
#endif
1237+
}
1238+
1239+
/* Print summary info about the state of the optimized allocator */
1240+
void
1241+
_PyTuple_DebugMallocStats(FILE *out)
1242+
{
1243+
#if PyTuple_MAXSAVESIZE > 0
1244+
PyInterpreterState *interp = _PyInterpreterState_GET();
1245+
for (int i = 0; i < PyTuple_MAXSAVESIZE; i++) {
1246+
int len = i + 1;
1247+
char buf[128];
1248+
PyOS_snprintf(buf, sizeof(buf),
1249+
"free %d-sized PyTupleObject", len);
1250+
_PyDebugAllocatorStats(out, buf, STATE.numfree[i],
1251+
_PyObject_VAR_SIZE(&PyTuple_Type, len));
1252+
}
1253+
#endif
1254+
}
1255+
1256+
#undef STATE
1257+
#undef FREELIST_FINALIZED

0 commit comments

Comments
 (0)