8000 bpo-40521: Make async gen free lists per-interpreter (GH-20643) · python/cpython@78a02c2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 78a02c2

Browse files
authored
bpo-40521: Make async gen free lists per-interpreter (GH-20643)
Each interpreter now has its own asynchronous generator free lists: * Move async gen free lists into PyInterpreterState. * Move _PyAsyncGen_MAXFREELIST define to pycore_interp.h * Add _Py_async_gen_state structure. * Add tstate parameter to _PyAsyncGen_ClearFreeLists and _PyAsyncGen_Fini().
1 parent 88ec919 commit 78a02c2

File tree

7 files changed

+67
-46
lines changed

7 files changed

+67
-46
lines changed

Include/internal/pycore_gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ extern void _PyTuple_ClearFreeList(PyThreadState *tstate);
170170
extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
171171
extern void _PyList_ClearFreeList(PyThreadState *tstate);
172172
extern void _PyDict_ClearFreeList(void);
173-
extern void _PyAsyncGen_ClearFreeLists(void);
173+
extern void _PyAsyncGen_ClearFreeLists(PyThreadState *tstate);
174174
extern void _PyContext_ClearFreeList(void);
175175

176176
#ifdef __cplusplus

Include/internal/pycore_interp.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ struct _Py_frame_state {
108108
int numfree;
109109
};
110110

111+
#ifndef _PyAsyncGen_MAXFREELIST
112+
# define _PyAsyncGen_MAXFREELIST 80
113+
#endif
114+
115+
struct _Py_async_gen_state {
116+
/* Freelists boost performance 6-10%; they also reduce memory
117+
fragmentation, as _PyAsyncGenWrappedValue and PyAsyncGenASend
118+
are short-living objects that are instantiated for every
119+
__anext__() call. */
120+
struct _PyAsyncGenWrappedValue* value_freelist[_PyAsyncGen_MAXFREELIST];
121+
int value_numfree;
122+
123+
struct PyAsyncGenASend* asend_freelist[_PyAsyncGen_MAXFREELIST];
124+
int asend_numfree;
125+
};
126+
127+
111128

112129
/* interpreter state */
113130

@@ -205,6 +222,7 @@ struct _is {
205222
struct _Py_list_state list;
206223
struct _Py_float_state float_state;
207224
struct _Py_frame_state frame;
225+
struct _Py_async_gen_state async_gen;
208226

209227
/* Using a cache is very effective since typically only a single slice is
210228
created and then deleted again. */

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ extern void _PySet_Fini(void);
6666
extern void _PyBytes_Fini(void);
6767
extern void _PyFloat_Fini(PyThreadState *tstate);
6868
extern void _PySlice_Fini(PyThreadState *tstate);
69-
extern void _PyAsyncGen_Fini(void);
69+
extern void _PyAsyncGen_Fini(PyThreadState *tstate);
7070

7171
extern void PyOS_FiniInterrupts(void);
7272

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
The tuple free lists, the empty tuple singleton, the list free list, the float
2-
free list, the slice cache, and the frame free list are no longer shared by all
3-
interpreters: each interpreter now its has own free lists and caches.
2+
free list, the slice cache, the frame free list, the asynchronous generator
3+
free lists are no longer shared by all interpreters: each interpreter now its
4+
has own free lists and caches.

Modules/gcmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,7 @@ clear_freelists(void)
10311031
_PyFloat_ClearFreeList(tstate);
10321032
_PyList_ClearFreeList(tstate);
10331033
_PyDict_ClearFreeList();
1034-
_PyAsyncGen_ClearFreeLists();
1034+
_PyAsyncGen_ClearFreeLists(tstate);
10351035
_PyContext_ClearFreeList();
10361036
}
10371037

Objects/genobject.c

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,7 @@ typedef enum {
11621162
} AwaitableState;
11631163

11641164

1165-
typedef struct {
1165+
typedef struct PyAsyncGenASend {
11661166
PyObject_HEAD
11671167
PyAsyncGenObject *ags_gen;
11681168

@@ -1174,7 +1174,7 @@ typedef struct {
11741174
} PyAsyncGenASend;
11751175

11761176

1177-
typedef struct {
1177+
typedef struct PyAsyncGenAThrow {
11781178
PyObject_HEAD
11791179
PyAsyncGenObject *agt_gen;
11801180

@@ -1186,28 +1186,12 @@ typedef struct {
11861186
} PyAsyncGenAThrow;
11871187

11881188

1189-
typedef struct {
1189+
typedef struct _PyAsyncGenWrappedValue {
11901190
PyObject_HEAD
11911191
PyObject *agw_val;
11921192
} _PyAsyncGenWrappedValue;
11931193

11941194

1195-
#ifndef _PyAsyncGen_MAXFREELIST
1196-
#define _PyAsyncGen_MAXFREELIST 80
1197-
#endif
1198-
1199-
/* Freelists boost performance 6-10%; they also reduce memory
1200-
fragmentation, as _PyAsyncGenWrappedValue and PyAsyncGenASend
1201-
are short-living objects that are instantiated for every
1202-
__anext__ call.
1203-
*/
1204-
1205-
static _PyAsyncGenWrappedValue *ag_value_freelist[_PyAsyncGen_MAXFREELIST];
1206-
static int ag_value_freelist_free = 0;
1207-
1208-
static PyAsyncGenASend *ag_asend_freelist[_PyAsyncGen_MAXFREELIST];
1209-
static int ag_asend_freelist_free = 0;
1210-
12111195
#define _PyAsyncGenWrappedValue_CheckExact(o) \
12121196
Py_IS_TYPE(o, &_PyAsyncGenWrappedValue_Type)
12131197

@@ -1423,27 +1407,29 @@ PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
14231407

14241408

14251409
void
1426-
_PyAsyncGen_ClearFreeLists(void)
1410+
_PyAsyncGen_ClearFreeLists(PyThreadState *tstate)
14271411
{
1428-
while (ag_value_freelist_free) {
1412+
struct _Py_async_gen_state *state = &tstate->interp->async_gen;
1413+
1414+
while (state->value_numfree) {
14291415
_PyAsyncGenWrappedValue *o;
1430-
o = ag_value_freelist[--ag_value_freelist_free];
1416+
o = state->value_freelist[--state->value_numfree];
14311417
assert(_PyAsyncGenWrappedValue_CheckExact(o));
14321418
PyObject_GC_Del(o);
14331419
}
14341420

1435-
while (ag_asend_freelist_free) {
1421+
while (state->asend_numfree) {
14361422
PyAsyncGenASend *o;
1437-
o = ag_asend_freelist[--ag_asend_freelist_free];
1423+
o = state->asend_freelist[--state->asend_numfree];
14381424
10000 assert(Py_IS_TYPE(o, &_PyAsyncGenASend_Type));
14391425
PyObject_GC_Del(o);
14401426
}
14411427
}
14421428

14431429
void
1444-
_PyAsyncGen_Fini(void)
1430+
_PyAsyncGen_Fini(PyThreadState *tstate)
14451431
{
1446-
_PyAsyncGen_ClearFreeLists();
1432+
_PyAsyncGen_ClearFreeLists(tstate);
14471433
}
14481434

14491435

@@ -1486,10 +1472,13 @@ async_gen_asend_dealloc(PyAsyncGenASend *o)
14861472
_PyObject_GC_UNTRACK((PyObject *)o);
14871473
Py_CLEAR(o->ags_gen);
14881474
Py_CLEAR(o->ags_sendval);
1489-
if (ag_asend_freelist_free < _PyAsyncGen_MAXFREELIST) {
1475+
PyInterpreterState *interp = _PyInterpreterState_GET();
1476+
struct _Py_async_gen_state *state = &interp->async_gen;
1477+
if (state->asend_numfree < _PyAsyncGen_MAXFREELIST) {
14901478
assert(PyAsyncGenASend_CheckExact(o));
1491-
ag_asend_freelist[ag_asend_freelist_free++] = o;
1492-
} else {
1479+
state->asend_freelist[state->asend_numfree++] = o;
1480+
}
1481+
else {
14931482
PyObject_GC_Del(o);
14941483
}
14951484
}
@@ -1641,11 +1630,14 @@ static PyObject *
16411630
async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval)
16421631
{
16431632
PyAsyncGenASend *o;
1644-
if (ag_asend_freelist_free) {
1645-
ag_asend_freelist_free--;
1646-
o = ag_asend_freelist[ag_asend_freelist_free];
1633+
PyInterpreterState *interp = _PyInterpreterState_GET();
1634+
struct _Py_async_gen_state *state = &interp->async_gen;
1635+
if (state->asend_numfree) {
1636+
state->asend_numfree--;
1637+
o = state->asend_freelist[state->asend_numfree];
16471638
_Py_NewReference((PyObject *)o);
1648-
} else {
1639+
}
1640+
else {
16491641
o = PyObject_GC_New(PyAsyncGenASend, &_PyAsyncGenASend_Type);
16501642
if (o == NULL) {
16511643
return NULL;
@@ -1673,10 +1665,13 @@ async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o)
16731665
{
16741666
_PyObject_GC_UNTRACK((PyObject *)o);
16751667
Py_CLEAR(o->agw_val);
1676-
if (ag_value_freelist_free < _PyAsyncGen_MAXFREELIST) {
1668+
PyInterpreterState *interp = _PyInterpreterState_GET();
1669+
struct _Py_async_gen_state *state = &interp->async_gen;
1670+
if (state->value_numfree < _PyAsyncGen_MAXFREELIST) {
16771671
assert(_PyAsyncGenWrappedValue_CheckExact(o));
1678-
ag_value_freelist[ag_value_freelist_free++] = o;
1679-
} else {
1672+
state->value_freelist[state->value_numfree++] = o;
1673+
}
1674+
else {
16801675
PyObject_GC_Del(o);
16811676
}
16821677
}
@@ -1740,12 +1735,15 @@ _PyAsyncGenValueWrapperNew(PyObject *val)
17401735
_PyAsyncGenWrappedValue *o;
17411736
assert(val);
17421737

1743-
if (ag_value_freelist_free) {
1744-
ag_value_freelist_free--;
1745-
o = ag_value_freelist[ag_value_freelist_free];
1738+
PyInterpreterState *interp = _PyInterpreterState_GET();
1739+
struct _Py_async_gen_state *state = &interp->async_gen;
1740+
if (state->value_numfree) {
1741+
state->value_numfree--;
1742+
o = state->value_freelist[state->value_numfree];
17461743
assert(_PyAsyncGenWrappedValue_CheckExact(o));
17471744
_Py_NewReference((PyObject*)o);
1748-
} else {
1745+
}
1746+
else {
17491747
o = PyObject_GC_New(_PyAsyncGenWrappedValue,
17501748
&_PyAsyncGenWrappedValue_Type);
17511749
if (o == NULL) {

Python/pylifecycle.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,11 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
12701270
if (is_main_interp) {
12711271
_Py_HashRandomization_Fini();
12721272
_PyArg_Fini();
1273-
_PyAsyncGen_Fini();
1273+
}
1274+
1275+
_PyAsyncGen_Fini(tstate);
1276+
1277+
if (is_main_interp) {
12741278
_PyContext_Fini();
12751279
}
12761280

0 commit comments

Comments
 (0)
0