Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 2 additions & 1 deletion Include/internal/pycore_qsbr.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ struct _qsbr_shared {
// Minimum observed read sequence of all QSBR thread states
uint64_t rd_seq;

// Array of QSBR thread states.
// Array of QSBR thread states (aligned to 64 bytes).
struct _qsbr_pad *array;
void *array_raw; // raw allocation pointer (for free)
Py_ssize_t size;

// Freelist of unused _qsbr_thread_states (protected by mutex)
Expand Down
6 changes: 6 additions & 0 deletions Include/internal/pycore_tstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ typedef struct _PyThreadStateImpl {
#if _Py_TIER2
struct _PyJitTracerState *jit_tracer_state;
#endif

#ifdef Py_GIL_DISABLED
// gh-144438: Add padding to ensure that the fields above don't share a
// cache line with other allocations.
char __padding[64];
#endif
} _PyThreadStateImpl;

#ifdef __cplusplus
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Align the QSBR thread state array to a 64-byte cache line boundary to
avoid false sharing in the free-threaded build.
19 changes: 13 additions & 6 deletions Python/qsbr.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,28 @@ grow_thread_array(struct _qsbr_shared *shared)
new_size = MIN_ARRAY_SIZE;
}

struct _qsbr_pad *array = PyMem_RawCalloc(new_size, sizeof(*array));
if (array == NULL) {
// Overallocate by 63 bytes so we can align to a 64-byte boundary.
// This avoids potential false sharing between the first entry and other
// allocations.
size_t alloc_size = (size_t)new_size * sizeof(struct _qsbr_pad) + 63;
void *raw = PyMem_RawCalloc(1, alloc_size);
if (raw == NULL) {
return -1;
}
struct _qsbr_pad *array = _Py_ALIGN_UP(raw, 64);

struct _qsbr_pad *old = shared->array;
if (old != NULL) {
void *old_raw = shared->array_raw;
if (shared->array != NULL) {
memcpy(array, shared->array, shared->size * sizeof(*array));
}

shared->array = array;
shared->array_raw = raw;
shared->size = new_size;
shared->freelist = NULL;
initialize_new_array(shared);

PyMem_RawFree(old);
PyMem_RawFree(old_raw);
return 0;
}

Expand Down Expand Up @@ -257,8 +263,9 @@ void
_Py_qsbr_fini(PyInterpreterState *interp)
{
struct _qsbr_shared *shared = &interp->qsbr;
PyMem_RawFree(shared->array);
PyMem_RawFree(shared->array_raw);
shared->array = NULL;
shared->array_raw = NULL;
shared->size = 0;
shared->freelist = NULL;
}
Expand Down
Loading