Skip to content
Open
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
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);
Comment on lines +90 to +96
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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);
// allocations.
size_t alignment = 64;
size_t alloc_size = (size_t)new_size * sizeof(struct _qsbr_pad) + alignment - 1;
void *raw = PyMem_RawCalloc(1, alloc_size);
if (raw == NULL) {
return -1;
}
struct _qsbr_pad *array = _Py_ALIGN_UP(raw, alignment);


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