Skip to content

Commit b408b2b

Browse files
committed
Cache borrowed references, cleared in func_dealloc
1 parent ece96f4 commit b408b2b

File tree

5 files changed

+23
-30
lines changed

5 files changed

+23
-30
lines changed

Include/internal/pycore_function.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ extern PyObject* _PyFunction_Vectorcall(
1919
#define FUNC_VERSION_CACHE_SIZE (1<<12) /* Must be a power of 2 */
2020
struct _py_func_state {
2121
uint32_t next_version;
22-
// Function objects whose func_version % FUNC_VERSION_CACHE_SIZE
23-
// once equaled the index in the table. The references are owned:
24-
// Call _PyFunction_ClearByVersionCache() to clear.
22+
// Borrowed references to function objects whose
23+
// func_version % FUNC_VERSION_CACHE_SIZE
24+
// once was equal to the index in the table.
25+
// They are cleared when the function is deallocated.
2526
PyFunctionObject *func_version_cache[FUNC_VERSION_CACHE_SIZE];
2627
};
2728

@@ -30,7 +31,6 @@ extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor *constr)
3031
extern uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);
3132
extern void _PyFunction_SetVersion(PyFunctionObject *func, uint32_t version);
3233
PyFunctionObject *_PyFunction_LookupByVersion(uint32_t version);
33-
void _PyFunction_ClearByVersionCache(PyInterpreterState *interp);
3434

3535
extern PyObject *_Py_set_function_type_params(
3636
PyThreadState* unused, PyObject *func, PyObject *type_params);

Objects/funcobject.c

+15-18
Original file line numberDiff line numberDiff line change
@@ -271,35 +271,23 @@ _PyFunction_SetVersion(PyFunctionObject *func, uint32_t version)
271271
func->func_version = version;
272272
if (version != 0) {
273273
PyInterpreterState *interp = _PyInterpreterState_GET();
274-
PyFunctionObject **slot =
275-
interp->func_state.func_version_cache
276-
+ (version % FUNC_VERSION_CACHE_SIZE);
277-
Py_XSETREF(*slot, (PyFunctionObject *)Py_NewRef(func));
274+
interp->func_state.func_version_cache[
275+
version % FUNC_VERSION_CACHE_SIZE] = func;
278276
}
279277
}
280278

281279
PyFunctionObject *
282280
_PyFunction_LookupByVersion(uint32_t version)
283281
{
284282
PyInterpreterState *interp = _PyInterpreterState_GET();
285-
PyFunctionObject **slot =
286-
interp->func_state.func_version_cache
287-
+ (version % FUNC_VERSION_CACHE_SIZE);
288-
if (*slot != NULL && (*slot)->func_version == version) {
289-
return (PyFunctionObject *)Py_NewRef(*slot);
283+
PyFunctionObject *func = interp->func_state.func_version_cache[
284+
version % FUNC_VERSION_CACHE_SIZE];
285+
if (func != NULL && func->func_version == version) {
286+
return (PyFunctionObject *)Py_NewRef(func);
290287
}
291288
return NULL;
292289
}
293290

294-
void
295-
_PyFunction_ClearByVersionCache(PyInterpreterState *interp)
296-
{
297-
for (int i = 0; i < FUNC_VERSION_CACHE_SIZE; i++) {
298-
PyFunctionObject **slot = interp->func_state.func_version_cache + i;
299-
Py_CLEAR(*slot);
300-
}
301-
}
302-
303291
uint32_t
304292
_PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
305293
{
@@ -929,6 +917,15 @@ func_dealloc(PyFunctionObject *op)
929917
if (op->func_weakreflist != NULL) {
930918
PyObject_ClearWeakRefs((PyObject *) op);
931919
}
920+
if (op->func_version != 0) {
921+
PyInterpreterState *interp = _PyInterpreterState_GET();
922+
PyFunctionObject **slot =
923+
interp->func_state.func_version_cache
924+
+ (op->func_version % FUNC_VERSION_CACHE_SIZE);
925+
if (*slot == op) {
926+
*slot = NULL;
927+
}
928+
}
932929
(void)func_clear(op);
933930
// These aren't cleared by func_clear().
934931
Py_DECREF(op->func_code);

Python/abstract_interp_cases.c.h

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/pylifecycle.c

-5
Original file line numberDiff line numberDiff line change
@@ -1767,11 +1767,6 @@ Py_FinalizeEx(void)
17671767
// XXX assert(_Py_IsMainInterpreter(tstate->interp));
17681768
// XXX assert(_Py_IsMainThread());
17691769

1770-
// The function version cache keeps functions alive, so clear it.
1771-
// It's used for optimizations, which we don't need in this stage.
1772-
tstate->interp->func_state.next_version = 0; // No more new versions
1773-
_PyFunction_ClearByVersionCache(tstate->interp);
1774-
17751770
// Block some operations.
17761771
tstate->interp->finalizing = 1;
17771772

Python/sysmodule.c

-3
Original file line numberDiff line numberDiff line change
@@ -2079,9 +2079,6 @@ sys__clear_type_cache_impl(PyObject *module)
20792079
/*[clinic end generated code: output=20e48ca54a6f6971 input=127f3e04a8d9b555]*/
20802080
{
20812081
PyType_ClearCache();
2082-
// Also clear the function-by-version cache
2083-
PyInterpreterState *interp = _PyInterpreterState_GET();
2084-
_PyFunction_ClearByVersionCache(interp);
20852082
Py_RETURN_NONE;
20862083
}
20872084

0 commit comments

Comments
 (0)