Skip to content

Commit 7dc9191

Browse files
committed
Make pystats global state be per-interpreter.
1 parent 1c194e2 commit 7dc9191

File tree

11 files changed

+153
-139
lines changed

11 files changed

+153
-139
lines changed

Include/cpython/pystats.h

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,10 @@ typedef struct _stats {
186186
FTStats ft_stats;
187187
#endif
188188
RareEventStats rare_event_stats;
189-
GCStats *gc_stats;
189+
GCStats gc_stats[3]; // must match NUM_GENERATIONS
190190
} PyStats;
191191

192192

193-
#ifdef Py_GIL_DISABLED
194-
195193
#if defined(HAVE_THREAD_LOCAL) && !defined(Py_BUILD_CORE_MODULE)
196194
extern _Py_thread_local PyStats *_Py_tss_stats;
197195
#endif
@@ -200,30 +198,15 @@ extern _Py_thread_local PyStats *_Py_tss_stats;
200198
// inline function.
201199
PyAPI_FUNC(PyStats *) _PyStats_GetLocal(void);
202200

203-
#else // !Py_GIL_DISABLED
204-
205-
// Export for shared extensions like 'math'
206-
PyAPI_DATA(PyStats*) _Py_stats;
207-
208-
#endif
209-
210201
// Return pointer to the PyStats structure, NULL if recording is off.
211202
static inline PyStats*
212203
_PyStats_GET(void)
213204
{
214-
#ifdef Py_GIL_DISABLED
215-
216205
#if defined(HAVE_THREAD_LOCAL) && !defined(Py_BUILD_CORE_MODULE)
217206
return _Py_tss_stats;
218207
#else
219208
return _PyStats_GetLocal();
220209
#endif
221-
222-
#else // !Py_GIL_DISABLED
223-
224-
return _Py_stats;
225-
226-
#endif
227210
}
228211

229212
#define _Py_STATS_EXPR(expr) \

Include/internal/pycore_interp_structs.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ enum _GCPhase {
198198
};
199199

200200
/* If we change this, we need to change the default value in the
201-
signature of gc.collect. */
201+
signature of gc.collect and change the size of PyStats.gc_stats */
202202
#define NUM_GENERATIONS 3
203203

204204
struct _gc_runtime_state {
@@ -968,6 +968,18 @@ struct _is {
968968
# ifdef Py_STACKREF_CLOSE_DEBUG
969969
_Py_hashtable_t *closed_stackrefs_table;
970970
# endif
971+
#endif
972+
973+
#ifdef Py_STATS
974+
// true if recording of pystats is on, this is used when new threads
975+
// are created to decide if recording should be on for them
976+
bool pystats_enabled;
977+
// allocated when (and if) stats are first enabled
978+
PyStats *pystats_struct;
979+
#ifdef Py_GIL_DISABLED
980+
// held when pystats related interpreter state is being updated
981+
PyMutex pystats_mutex;
982+
#endif
971983
#endif
972984

973985
/* the initial PyInterpreterState.threads.head */

Include/internal/pycore_pystats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extern "C" {
99
#endif
1010

1111
#ifdef Py_STATS
12-
extern void _Py_StatsOn(void);
12+
extern int _Py_StatsOn(void);
1313
extern void _Py_StatsOff(void);
1414
extern void _Py_StatsClear(void);
1515
extern int _Py_PrintSpecializationStats(int to_file);

Include/internal/pycore_stats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
110110
RARE_EVENT_INTERP_INC(interp, name); \
111111
} while (0); \
112112

113-
bool _PyStats_ThreadInit(_PyThreadStateImpl *);
113+
bool _PyStats_ThreadInit(PyInterpreterState *, _PyThreadStateImpl *);
114114
void _PyStats_ThreadFini(_PyThreadStateImpl *);
115115
void _PyStats_Attach(_PyThreadStateImpl *);
116116
void _PyStats_Detach(_PyThreadStateImpl *);

Include/internal/pycore_tstate.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,16 @@ 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+
7476
#ifdef Py_STATS
75-
// per-thread stats, will be merged into the _Py_stats_struct global
77+
#ifdef Py_GIL_DISABLED
78+
// per-thread stats, will be merged into interp->pystats_struct
7679
PyStats *pystats_struct; // allocated by _PyStats_ThreadInit()
80+
#endif
7781
PyStats **pystats_tss; // pointer to tss variable
7882
#endif
7983

80-
#endif // Py_GIL_DISABLED
81-
8284
#if defined(Py_REF_DEBUG) && defined(Py_GIL_DISABLED)
8385
Py_ssize_t reftotal; // this thread's total refcount operations
8486
#endif

Objects/object.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2418,6 +2418,13 @@ _PyObject_InitState(PyInterpreterState *interp)
24182418
if (refchain_init(interp) < 0) {
24192419
return _PyStatus_NO_MEMORY();
24202420
}
2421+
#endif
2422+
#ifdef Py_STATS
2423+
if (interp->config._pystats) {
2424+
// start with pystats enabled, can be disabled via sys._stats_off()
2425+
// this needs to be set before the first tstate is created
2426+
interp->pystats_enabled = true;
2427+
}
24212428
#endif
24222429
return _PyStatus_OK();
24232430
}

Python/initconfig.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2807,12 +2807,6 @@ _PyConfig_Write(const PyConfig *config, _PyRuntimeState *runtime)
28072807
return _PyStatus_NO_MEMORY();
28082808
}
28092809

2810-
#ifdef Py_STATS
2811-
if (config->_pystats) {
2812-
_Py_StatsOn();
2813-
}
2814-
#endif
2815-
28162810
return _PyStatus_OK();
28172811
}
28182812

Python/pystate.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@
2222
#include "pycore_runtime.h" // _PyRuntime
2323
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
2424
#include "pycore_stackref.h" // Py_STACKREF_DEBUG
25+
#include "pycore_stats.h" // FT_STAT_WORLD_STOP_INC()
2526
#include "pycore_time.h" // _PyTime_Init()
2627
#include "pycore_uniqueid.h" // _PyObject_FinalizePerThreadRefcounts()
27-
#include "pycore_stats.h" // FT_STAT_WORLD_STOP_INC()
28-
2928

3029

3130
/* --------------------------------------------------------------------------
@@ -480,6 +479,12 @@ alloc_interpreter(void)
480479
static void
481480
free_interpreter(PyInterpreterState *interp)
482481
{
482+
#ifdef Py_STATS
483+
if (interp->pystats_struct) {
484+
PyMem_RawFree(interp->pystats_struct);
485+
interp->pystats_struct = NULL;
486+
}
487+
#endif
483488
// The main interpreter is statically allocated so
484489
// should not be freed.
485490
if (interp != &_PyRuntime._main_interpreter) {
@@ -1532,7 +1537,7 @@ new_threadstate(PyInterpreterState *interp, int whence)
15321537
#endif
15331538
#ifdef Py_STATS
15341539
// The PyStats structure is quite large and is allocated separated from tstate.
1535-
if (!_PyStats_ThreadInit(tstate)) {
1540+
if (!_PyStats_ThreadInit(interp, tstate)) {
15361541
free_threadstate(tstate);
15371542
return NULL;
15381543
}
@@ -2017,9 +2022,6 @@ tstate_activate(PyThreadState *tstate)
20172022
if (!tstate->_status.bound_gilstate) {
20182023
bind_gilstate_tstate(tstate);
20192024
}
2020-
#ifdef Py_STATS
2021-
_PyStats_Attach((_PyThreadStateImpl *)tstate);
2022-
#endif
20232025

20242026
tstate->_status.active = 1;
20252027
}
@@ -2139,6 +2141,10 @@ _PyThreadState_Attach(PyThreadState *tstate)
21392141
_PyCriticalSection_Resume(tstate);
21402142
}
21412143

2144+
#ifdef Py_STATS
2145+
_PyStats_Attach((_PyThreadStateImpl *)tstate);
2146+
#endif
2147+
21422148
#if defined(Py_DEBUG)
21432149
errno = err;
21442150
#endif

0 commit comments

Comments
 (0)