Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-94673: Recover Weaklist Lookup Performance #95544

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,16 @@ extern void _Py_PrintReferences(FILE *);
extern void _Py_PrintReferenceAddresses(FILE *);
#endif


/* Return the *address* of the object's weaklist. The address may be
* dereferenced to get the current head of the weaklist. This is useful
* for iterating over the linked list of weakrefs, especially when the
* list is being modified externally (e.g. refs getting removed).
*
* The returned pointer should not be used to change the head of the list
* nor should it be used to add, remove, or swap any refs in the list.
* That is the sole responsibility of the code in weakrefobject.c.
*/
static inline PyObject **
_PyObject_GET_WEAKREFS_LISTPTR(PyObject *op)
{
Expand All @@ -226,10 +236,33 @@ _PyObject_GET_WEAKREFS_LISTPTR(PyObject *op)
(PyTypeObject *)op);
return _PyStaticType_GET_WEAKREFS_LISTPTR(state);
}
// Essentially _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET():
Py_ssize_t offset = Py_TYPE(op)->tp_weaklistoffset;
return (PyObject **)((char *)op + offset);
}

/* This is a special case of _PyObject_GET_WEAKREFS_LISTPTR().
* Only the most fundamental lookup path is used.
* Consequently, static types should not be used.
*
* For static builtin types the returned pointer will always point
* to a NULL tp_weaklist. This is fine for any deallocation cases,
* since static types are never deallocated and static builtin types
* are only finalized at the end of runtime finalization.
*
* If the weaklist for static types is actually needed then use
* _PyObject_GET_WEAKREFS_LISTPTR().
*/
static inline PyWeakReference **
_PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(PyObject *op)
{
assert(!PyType_Check(op) ||
((PyTypeObject *)op)->tp_flags & Py_TPFLAGS_HEAPTYPE);
Py_ssize_t offset = Py_TYPE(op)->tp_weaklistoffset;
return (PyWeakReference **)((char *)op + offset);
}


// Fast inlined version of PyObject_IS_GC()
static inline int
_PyObject_IS_GC(PyObject *obj)
Expand Down
9 changes: 6 additions & 3 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,9 +794,12 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
if (! _PyType_SUPPORTS_WEAKREFS(Py_TYPE(op)))
continue;

/* It supports weakrefs. Does it have any? */
wrlist = (PyWeakReference **)
_PyObject_GET_WEAKREFS_LISTPTR(op);
/* It supports weakrefs. Does it have any?
*
* This is never triggered for static types so we can avoid the
* (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR().
*/
wrlist = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(op);

/* `op` may have some weakrefs. March over the list, clear
* all the weakrefs, and move the weakrefs with callbacks
Expand Down
12 changes: 8 additions & 4 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1507,11 +1507,15 @@ subtype_dealloc(PyObject *self)
finalizers since they might rely on part of the object
being finalized that has already been destroyed. */
if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
/* Modeled after GET_WEAKREFS_LISTPTR() */
PyWeakReference **list = (PyWeakReference **) \
_PyObject_GET_WEAKREFS_LISTPTR(self);
while (*list)
/* Modeled after GET_WEAKREFS_LISTPTR().

This is never triggered for static types so we can avoid the
(slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). */
PyWeakReference **list = \
_PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(self);
while (*list) {
_PyWeakref_ClearRef(*list);
}
}
}

Expand Down