gh-159860: Adjust QSBR cache line size from 64 to 128 on AArch64 (gh-159862)

This commit is contained in:
Gr4ph0s authored and GitHub committed 2026-10-20 20:06:55 +00:00
1 parent 74367077b8
commit 39bc958bf9
4 files changed
+24 -5

No files matched your search

+17 -2
View File
@@ -69,10 +69,25 @@ struct _qsbr_thread_state {
struct _qsbr_thread_state *freelist_next;
};
// Size used to pad and align the per-thread QSBR states (and the end of
// _PyThreadStateImpl) to avoid false sharing. Apple Silicon has 128-byte cache
// lines:
// $ sysctl -a hw | grep cachelinesize
// hw.cachelinesize: 128
// With 64-byte padding the `seq` fields of two threads share a line and the
// stores in _Py_qsbr_quiescent_state() contend on every eval loop back-edge.
// Use 128 bytes for all AArch64 targets: on cores with 64-byte lines this only
// costs 64 bytes per thread.
#if defined(__aarch64__) || defined(_M_ARM64)
# define _Py_QSBR_CACHE_LINE_SIZE 128
#else
# define _Py_QSBR_CACHE_LINE_SIZE 64
#endif
// Padding to avoid false sharing
struct _qsbr_pad {
struct _qsbr_thread_state qsbr;
char __padding[64 - sizeof(struct _qsbr_thread_state)];
char __padding[_Py_QSBR_CACHE_LINE_SIZE - sizeof(struct _qsbr_thread_state)];
};
// Per-interpreter state
@@ -83,7 +98,7 @@ struct _qsbr_shared {
// Minimum observed read sequence of all QSBR thread states
uint64_t rd_seq;
// Array of QSBR thread states (aligned to 64 bytes).
// Array of QSBR thread states (aligned to _Py_QSBR_CACHE_LINE_SIZE).
struct _qsbr_pad *array;
void *array_raw; // raw allocation pointer (for free)
Py_ssize_t size;
+1 -1
View File
@@ -109,7 +109,7 @@ typedef struct _PyThreadStateImpl {
#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];
char __padding[_Py_QSBR_CACHE_LINE_SIZE];
#endif
} _PyThreadStateImpl;
@@ -0,0 +1,4 @@
Fix false sharing between the per-thread QSBR states in the
:term:`free-threaded build` on AArch64 CPUs with 128-byte cache lines, such
as Apple Silicon. CPU-bound threads that share no objects now scale like
processes there.
+2 -2
View File
@@ -85,10 +85,10 @@ grow_thread_array(struct _qsbr_shared *shared)
new_size = MIN_ARRAY_SIZE;
}
// Overallocate by 63 bytes so we can align to a 64-byte boundary.
// Overallocate so we can align to a cache line boundary.
// This avoids potential false sharing between the first entry and other
// allocations.
size_t alignment = 64;
size_t alignment = _Py_QSBR_CACHE_LINE_SIZE;
size_t alloc_size = (size_t)new_size * sizeof(struct _qsbr_pad) + alignment - 1;
void *raw = PyMem_RawCalloc(1, alloc_size);
if (raw == NULL) {