Skip to content

Commit 3f58012

Browse files
committed
Remove _Py_tss_stats thread local.
Add pystats pointer to PyThreadState and use from there instead. This is slightly slower but shouldn't matter in practice. This simplifies the attach/detach logic as well.
1 parent c94e5c4 commit 3f58012

File tree

5 files changed

+46
-42
lines changed

5 files changed

+46
-42
lines changed

Include/cpython/pystate.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ struct _ts {
208208
*/
209209
PyObject *threading_local_sentinel;
210210
_PyRemoteDebuggerSupport remote_debugger_support;
211+
212+
#ifdef Py_STATS
213+
PyStats *pystats; // pointer PyStats structure, NULL if recording is off
214+
#endif
211215
};
212216

213217
/* other API */
@@ -230,6 +234,19 @@ PyAPI_FUNC(void) PyThreadState_EnterTracing(PyThreadState *tstate);
230234
// function is set, otherwise disable them.
231235
PyAPI_FUNC(void) PyThreadState_LeaveTracing(PyThreadState *tstate);
232236

237+
#if defined(HAVE_THREAD_LOCAL) && !defined(Py_BUILD_CORE_MODULE)
238+
extern _Py_thread_local PyThreadState* _Py_tss_tstate;
239+
240+
static inline PyStats*
241+
_PyThreadState_GetStatsFast(void)
242+
{
243+
if (_Py_tss_tstate == NULL) {
244+
return NULL; // no attached thread state
245+
}
246+
return _Py_tss_tstate->pystats;
247+
}
248+
#endif
249+
233250
/* PyGILState */
234251

235252
/* Helper/diagnostic function - return 1 if the current thread

Include/cpython/pystats.h

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// - sys._stats_dump()
1515
//
1616
// Python must be built with ./configure --enable-pystats to define the
17-
// _PyStats_GET() function.
17+
// _PyStats_GET() macro.
1818
//
1919
// Define _PY_INTERPRETER macro to increment interpreter_increfs and
2020
// interpreter_decrefs. Otherwise, increment increfs and decrefs.
@@ -112,8 +112,11 @@ typedef struct _gc_stats {
112112
#ifdef Py_GIL_DISABLED
113113
// stats specific to free-threaded build
114114
typedef struct _ft_stats {
115+
// number of times interpreter had to spin or park when trying to acquire a mutex
115116
uint64_t mutex_sleeps;
117+
// number of times that the QSBR mechanism polled (compute read sequence value)
116118
uint64_t qsbr_polls;
119+
// number of times stop-the-world mechanism was used
117120
uint64_t world_stops;
118121
} FTStats;
119122
#endif
@@ -189,25 +192,16 @@ typedef struct _stats {
189192
GCStats gc_stats[3]; // must match NUM_GENERATIONS
190193
} PyStats;
191194

192-
193-
#if defined(HAVE_THREAD_LOCAL) && !defined(Py_BUILD_CORE_MODULE)
194-
extern _Py_thread_local PyStats *_Py_tss_stats;
195-
#endif
196-
197-
// Export for most shared extensions, used via _PyStats_GET() static
198-
// inline function.
195+
// Export for most shared extensions
199196
PyAPI_FUNC(PyStats *) _PyStats_GetLocal(void);
200197

201-
// Return pointer to the PyStats structure, NULL if recording is off.
202-
static inline PyStats*
203-
_PyStats_GET(void)
204-
{
205198
#if defined(HAVE_THREAD_LOCAL) && !defined(Py_BUILD_CORE_MODULE)
206-
return _Py_tss_stats;
199+
// use inline function version defined in cpython/pystate.h
200+
static inline PyStats* _PyThreadState_GetStatsFast(void);
201+
#define _PyStats_GET _PyThreadState_GetStatsFast
207202
#else
208-
return _PyStats_GetLocal();
203+
#define _PyStats_GET _PyStats_GetLocal
209204
#endif
210-
}
211205

212206
#define _Py_STATS_EXPR(expr) \
213207
do { \

Include/internal/pycore_tstate.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,13 @@ typedef struct _PyThreadStateImpl {
7171
// When >1, code objects do not immortalize their non-string constants.
7272
int suppress_co_const_immortalization;
7373

74-
#endif // Py_GIL_DISABLED
75-
7674
#ifdef Py_STATS
77-
#ifdef Py_GIL_DISABLED
78-
// per-thread stats, will be merged into interp->pystats_struct
79-
PyStats *pystats_struct; // allocated by _PyStats_ThreadInit()
80-
#endif
81-
PyStats **pystats_tss; // pointer to tss variable
75+
// per-thread stats, will be merged into interp->pystats_struct
76+
PyStats *pystats_struct; // allocated by _PyStats_ThreadInit()
8277
#endif
8378

79+
#endif // Py_GIL_DISABLED
80+
8481
#if defined(Py_REF_DEBUG) && defined(Py_GIL_DISABLED)
8582
Py_ssize_t reftotal; // this thread's total refcount operations
8683
#endif

Python/pystats.c

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "pycore_opcode_metadata.h" // _PyOpcode_Caches
44
#include "pycore_pyatomic_ft_wrappers.h"
55
#include "pycore_pylifecycle.h" // _PyOS_URandomNonblock()
6+
#include "pycore_tstate.h"
67
#include "pycore_uop_metadata.h" // _PyOpcode_uop_name
78
#include "pycore_uop_ids.h" // MAX_UOP_ID
89
#include "pycore_pystate.h" // _PyThreadState_GET()
@@ -12,13 +13,14 @@
1213

1314
#ifdef Py_STATS
1415

15-
// Pointer to Thread-local stats structure, null if recording is off.
16-
_Py_thread_local PyStats *_Py_tss_stats;
17-
1816
PyStats *
1917
_PyStats_GetLocal(void)
2018
{
21-
return _Py_tss_stats;
19+
PyThreadState *tstate = _PyThreadState_GET();
20+
if (tstate) {
21+
return tstate->pystats;
22+
}
23+
return NULL;
2224
}
2325

2426
#ifdef Py_GIL_DISABLED
@@ -582,20 +584,18 @@ stats_toggle_on_off(PyThreadState *tstate, int on)
582584
if (!ts->_status.active) {
583585
continue;
584586
}
585-
_PyThreadStateImpl *ts_impl = (_PyThreadStateImpl *)ts;
586587
PyStats *s;
587588
if (interp->pystats_enabled) {
588589
#ifdef Py_GIL_DISABLED
589-
s = ts_impl->pystats_struct;
590+
s = ((_PyThreadStateImpl *)ts)->pystats_struct;
590591
#else
591592
s = ((PyThreadState *)tstate)->interp->pystats_struct;
592593
#endif
593594
}
594595
else {
595596
s = NULL;
596597
}
597-
// write to the tss variable for the 'ts' thread
598-
*ts_impl->pystats_tss = s;
598+
ts->pystats = s;
599599
}
600600
_PyEval_StartTheWorld(interp);
601601
return 0;
@@ -759,31 +759,28 @@ _PyStats_ThreadFini(_PyThreadStateImpl *tstate)
759759
}
760760

761761
void
762-
_PyStats_Attach(_PyThreadStateImpl *tstate)
762+
_PyStats_Attach(_PyThreadStateImpl *tstate_impl)
763763
{
764764
PyStats *s;
765-
PyInterpreterState *interp = ((PyThreadState *)tstate)->interp;
765+
PyThreadState *tstate = (PyThreadState *)tstate_impl;
766+
PyInterpreterState *interp = tstate->interp;
766767
if (FT_ATOMIC_LOAD_INT_RELAXED(interp->pystats_enabled)) {
767768
#ifdef Py_GIL_DISABLED
768-
s = tstate->pystats_struct;
769+
s = ((_PyThreadStateImpl *)tstate)->pystats_struct;
769770
#else
770-
s = ((PyThreadState *)tstate)->interp->pystats_struct;
771+
s = tstate->interp->pystats_struct;
771772
#endif
772773
}
773774
else {
774775
s = NULL;
775776
}
776-
// use correct TSS variable for thread
777-
tstate->pystats_tss = &_Py_tss_stats;
778-
// write to the tss variable for the 'ts' thread
779-
_Py_tss_stats = s;
777+
tstate->pystats = s;
780778
}
781779

782780
void
783-
_PyStats_Detach(_PyThreadStateImpl *tstate)
781+
_PyStats_Detach(_PyThreadStateImpl *tstate_impl)
784782
{
785-
tstate->pystats_tss = NULL;
786-
_Py_tss_stats = NULL;
783+
((PyThreadState *)tstate_impl)->pystats = NULL;
787784
}
788785

789786
#endif // Py_STATS

Tools/c-analyzer/cpython/ignored.tsv

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ Python/pyfpe.c - PyFPE_counter -
194194
Python/import.c - pkgcontext -
195195
Python/pystate.c - _Py_tss_tstate -
196196
Python/pystate.c - _Py_tss_gilstate -
197-
Python/pystats.c - _Py_tss_stats -
198197

199198
##-----------------------
200199
## should be const

0 commit comments

Comments
 (0)