10000 gh-124218: Refactor per-thread reference counting by colesbury · Pull Request #124844 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-124218: Refactor per-thread reference counting #124844

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

Merged
merged 4 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Con 10000 versations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Rename functions and types
  • Loading branch information
colesbury committed Oct 1, 2024
commit 77a8144e43e993715f8c527693b86bb3be658e4b
4 changes: 2 additions & 2 deletions Include/internal/pycore_interp.h
8000
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ extern "C" {
#include "pycore_qsbr.h" // struct _qsbr_state
#include "pycore_tstate.h" // _PyThreadStateImpl
#include "pycore_tuple.h" // struct _Py_tuple_state
#include "pycore_uniqueid.h" // struct _Py_type_id_pool
#include "pycore_uniqueid.h" // struct _Py_unique_id_pool
#include "pycore_typeobject.h" // struct types_state
#include "pycore_unicodeobject.h" // struct _Py_unicode_state
#include "pycore_warnings.h" // struct _warnings_runtime_state
Expand Down Expand Up @@ -221,7 +221,7 @@ struct _is {
#if defined(Py_GIL_DISABLED)
struct _mimalloc_interp_state mimalloc;
struct _brc_state brc; // biased reference counting state
struct _Py_type_id_pool type_ids;
struct _Py_unique_id_pool unique_ids; // object ids for per-thread refcounts
PyMutex weakref_locks[NUM_WEAKREF_LIST_LOCKS];
#endif

Expand Down
8 changes: 4 additions & 4 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,12 +335,12 @@ _Py_INCREF_TYPE(PyTypeObject *type)
// Unsigned comparison so that `unique_id=-1`, which indicates that
// per-thread refcounting has been disabled on this type, is handled by
// the "else".
if ((size_t)ht->unique_id < (size_t)tstate->types.size) {
if ((size_t)ht->unique_id < (size_t)tstate->refcounts.size) {
# ifdef Py_REF_DEBUG
_Py_INCREF_IncRefTotal();
# endif
_Py_INCREF_STAT_INC();
tstate->types.refcounts[ht->unique_id]++;
tstate->refcounts.values[ht->unique_id]++;
}
else {
// The slow path resizes the thread-local refcount array if necessary.
Expand Down Expand Up @@ -368,12 +368,12 @@ _Py_DECREF_TYPE(PyTypeObject *type)
// Unsigned comparison so that `unique_id=-1`, which indicates that
// per-thread refcounting has been disabled on this type, is handled by
// the "else".
if ((size_t)ht->unique_id < (size_t)tstate->types.size) {
if ((size_t)ht->unique_id < (size_t)tstate->refcounts.size) {
# ifdef Py_REF_DEBUG
_Py_DECREF_DecRefTotal();
# endif
_Py_DECREF_STAT_INC();
tstate->types.refcounts[ht->unique_id]--;
tstate->refcounts.values[ht->unique_id]--;
}
else {
// Directly decref the type if the type id is not assigned or if
Expand Down
8000
8 changes: 4 additions & 4 deletions Include/internal/pycore_tstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ typedef struct _PyThreadStateImpl {
struct _Py_freelists freelists;
struct _brc_thread_state brc;
struct {
// The thread-local refcounts for heap type objects
Py_ssize_t *refcounts;
// The per-thread refcounts
Py_ssize_t *values;

// Size of the refcounts array.
Py_ssize_t size;

// If set, don't use thread-local refcounts
// If set, don't use per-thread refcounts
int is_finalized;
} types;
} refcounts;
#endif

#if defined(Py_REF_DEBUG) && defined(Py_GIL_DISABLED)
Expand Down
59 changes: 28 additions & 31 deletions Include/internal/pycore_uniqueid.h
< 8000 td id="diff-7d48aa47a6858a78ec802b2d117004bb0aab672ba4ff0860ffc6e0072b34453cL68" data-line-number="68" class="blob-num blob-num-context js-linkable-line-number">
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,58 @@ extern "C" {

#ifdef Py_GIL_DISABLED

// This contains code for allocating unique ids to heap type objects
// and re-using those ids when the type is deallocated.
// This contains code for allocating unique ids to objects for
// per-thread reference counting.
//
// The type ids are used to implement per-thread reference counts of
// heap type objects to avoid contention on the reference count fields
// of heap type objects. Static type objects are immortal, so contention
// is not an issue for those types.
// Per-thread reference counting is used along with deferred reference
// counting to avoid scaling bottlenecks due to reference count contention.
//
// Type id of -1 is used to indicate a type doesn't use thread-local
// refcounting. This value is used when a type object is finalized by the GC
// and during interpreter shutdown to allow the type object to be
// An id of -1 is used to indicate that an object doesn't use per-thread
// refcounting. This value is used when the object is finalized by the GC
// and during interpreter shutdown to allow the object to be
// deallocated promptly when the object's refcount reaches zero.
//
// Each entry implicitly represents a type id based on it's offset in the
// Each entry implicitly represents a unique id based on its offset in the
// table. Non-allocated entries form a free-list via the 'next' pointer.
// Allocated entries store the corresponding PyTypeObject.
typedef union _Py_type_id_entry {
// Allocated entries store the corresponding PyObject.
typedef union _Py_unique_id_entry {
// Points to the next free type id, when part of the freelist
union _Py_type_id_entry *next;
union _Py_unique_id_entry *next;

// Stores the type object when the id is assigned
PyHeapTypeObject *type;
} _Py_type_id_entry;
// Stores the object when the id is assigned
PyObject *obj;
} _Py_unique_id_entry;

struct _Py_type_id_pool {
struct _Py_unique_id_pool {
PyMutex mutex;

// combined table of types with allocated type ids and unallocated
// type ids.
_Py_type_id_entry *table;
// combined table of object with allocated unique ids and unallocated ids.
_Py_unique_id_entry *table;

// Next entry to allocate inside 'table' or NULL
_Py_type_id_entry *freelist;
_Py_unique_id_entry *freelist;

// size of 'table'
Py_ssize_t size;
};

// Assigns the next id from the pool of type ids.
extern void _PyType_AssignId(PyHeapTypeObject *type);
// Assigns the next id from the pool of ids.
extern Py_ssize_t _PyObject_AssignUniqueId(PyObject *obj);

// Releases the allocated type id back to the pool.
extern void _PyType_ReleaseId(PyHeapTypeObject *type);
// Releases the allocated id back to the pool.
extern void _PyObject_ReleaseUniqueId(Py_ssize_t unique_id);

// Merges the thread-local reference counts into the corresponding types.
extern void _PyType_MergeThreadLocalRefcounts(_PyThreadStateImpl *tstate);
// Merges the per-thread reference counts into the corresponding objects.
extern void _PyObject_MergePerThreadRefcounts(_PyThreadStateImpl *tstate);

// Like _PyType_MergeThreadLocalRefcounts, but also frees the thread-local
// Like _PyObject_MergePerThreadRefcounts, but also frees the per-thread
// array of refcounts.
extern void _PyType_FinalizeThreadLocalRefcounts(_PyThreadStateImpl *tstate);
extern void _PyObject_FinalizePerThreadRefcounts(_PyThreadStateImpl *tstate);

// Frees the interpreter's pool of type ids.
extern void _PyType_FinalizeIdPool(PyInterpreterState *interp);
extern void _PyObject_FinalizeUniqueIdPool(PyInterpreterState *interp);

// Increfs the type, resizing the thread-local refcount array if necessary.
// Increfs the type, resizing the per-thread refcount array if necessary.
PyAPI_FUNC(void) _PyType_IncrefSlow(PyHeapTypeObject *type);

#endif /* Py_GIL_DISABLED */
Expand Down
6 changes: 3 additions & 3 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3932,7 +3932,7 @@ type_new_alloc(type_new_ctx *ctx)
et->ht_token = NULL;

#ifdef Py_GIL_DISABLED
_PyType_AssignId(et);
et->unique_id = _PyObject_AssignUniqueId((PyObject *)et);
#endif

return type;
Expand Down Expand Up @@ -5026,7 +5026,7 @@ PyType_FromMetaclass(

#ifdef Py_GIL_DISABLED
// Assign a type id to enable thread-local refcounting
_PyType_AssignId(res);
res->unique_id = _PyObject_AssignUniqueId((PyObject *)res);
#endif

/* Ready the type (which includes inheritance).
Expand Down Expand Up @@ -6080,7 +6080,7 @@ type_dealloc(PyObject *self)
Py_XDECREF(et->ht_module);
PyMem_Free(et->_ht_tpname);
#ifdef Py_GIL_DISABLED
_PyType_ReleaseId(et);
_PyObject_ReleaseUniqueId(et->unique_id);
#endif
et->ht_token = NULL;
Py_TYPE(type)->tp_free((PyObject *)type);
Expand Down
12 changes: 6 additions & 6 deletions Python/gc_free_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,12 @@ disable_deferred_refcounting(PyObject *op)
merge_refcount(op, 0);
}

// Heap types also use thread-local refcounting -- disable it here.
// Heap types also use per-thread refcounting -- disable it here.
if (PyType_Check(op)) {
// Disable thread-local refcounting for heap types
PyTypeObject *type = (PyTypeObject *)op;
if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
_PyType_ReleaseId((PyHeapTypeObject *)op);
if (PyType_HasFeature((PyTypeObject *)op, Py_TPFLAGS_HEAPTYPE)) {
PyHeapTypeObject *ht = (PyHeapTypeObject *)op;
_PyObject_ReleaseUniqueId(ht->unique_id);
ht->unique_id = -1;
}
}

Expand Down Expand Up @@ -1221,7 +1221,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p;

// merge per-thread refcount for types into the type's actual refcount
_PyType_MergeThreadLocalRefcounts(tstate);
_PyObject_MergePerThreadRefcounts(tstate);

// merge refcounts for all queued objects
merge_queued_objects(tstate, state);
Expand Down
2 changes: 1 addition & 1 deletion Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -1834,7 +1834,7 @@ finalize_interp_types(PyInterpreterState *interp)

_PyTypes_Fini(interp);
#ifdef Py_GIL_DISABLED
_PyType_FinalizeIdPool(interp);
_PyObject_FinalizeUniqueIdPool(interp);
#endif

_PyCode_Fini(interp);
Expand Down
4 changes: 2 additions & 2 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1745,7 +1745,7 @@ PyThreadState_Clear(PyThreadState *tstate)

// Merge our thread-local refcounts into the type's own refcount and
// free our local refcount array.
_PyType_FinalizeThreadLocalRefcounts((_PyThreadStateImpl *)tstate);
_PyObject_FinalizePerThreadRefcounts((_PyThreadStateImpl *)tstate);

// Remove ourself from the biased reference counting table of threads.
_Py_brc_remove_thread(tstate);
Expand Down Expand Up @@ -1805,7 +1805,7 @@ tstate_delete_common(PyThreadState *tstate, int release_gil)
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
tstate->interp->object_state.reftotal += tstate_impl->reftotal;
tstate_impl->reftotal = 0;
assert(tstate_impl->types.refcounts == NULL);
assert(tstate_impl->refcounts.values == NULL);
#endif

HEAD_UNLOCK(runtime);
Expand Down
Loading
Loading
0