mirror of
https://github.com/python/cpython
synced 2026-09-29 10:35:30 +03:00
gh-108753: Enhance pystats (#108754)
Statistics gathering is now off by default. Use the "-X pystats" command line option or set the new PYTHONSTATS environment variable to 1 to turn statistics gathering on at Python startup. Statistics are no longer dumped at exit if statistics gathering was off or statistics have been cleared. Changes: * Add PYTHONSTATS environment variable. * sys._stats_dump() now returns False if statistics are not dumped because they are all equal to zero. * Add PyConfig._pystats member. * Add tests on sys functions and on setting PyConfig._pystats to 1. * Add Include/cpython/pystats.h and Include/internal/pycore_pystats.h header files. * Rename '_py_stats' variable to '_Py_stats'. * Exclude Include/cpython/pystats.h from the Py_LIMITED_API. * Move pystats.h include from object.h to Python.h. * Add _Py_StatsOn() and _Py_StatsOff() functions. Remove '_py_stats_struct' variable from the API: make it static in specialize.c. * Document API in Include/pystats.h and Include/cpython/pystats.h. * Complete pystats documentation in Doc/using/configure.rst. * Don't write "all zeros" stats: if _stats_off() and _stats_clear() or _stats_dump() were called. * _PyEval_Fini() now always call _Py_PrintSpecializationStats() which does nothing if stats are all zeros. Co-authored-by: Michael Droettboom <mdboom@gmail.com>
This commit is contained in:
parent
8ff1142578
commit
a0773b89df
19 changed files with 403 additions and 184 deletions
|
|
@ -215,6 +215,11 @@ typedef struct PyConfig {
|
|||
|
||||
// If non-zero, we believe we're running from a source tree.
|
||||
int _is_python_build;
|
||||
|
||||
#ifdef Py_STATS
|
||||
// If non-zero, turns on statistics gathering.
|
||||
int _pystats;
|
||||
#endif
|
||||
} PyConfig;
|
||||
|
||||
PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config);
|
||||
|
|
|
|||
120
Include/cpython/pystats.h
Normal file
120
Include/cpython/pystats.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// Statistics on Python performance.
|
||||
//
|
||||
// API:
|
||||
//
|
||||
// - _Py_INCREF_STAT_INC() and _Py_DECREF_STAT_INC() used by Py_INCREF()
|
||||
// and Py_DECREF().
|
||||
// - _Py_stats variable
|
||||
//
|
||||
// Functions of the sys module:
|
||||
//
|
||||
// - sys._stats_on()
|
||||
// - sys._stats_off()
|
||||
// - sys._stats_clear()
|
||||
// - sys._stats_dump()
|
||||
//
|
||||
// Python must be built with ./configure --enable-pystats to define the
|
||||
// Py_STATS macro.
|
||||
//
|
||||
// Define _PY_INTERPRETER macro to increment interpreter_increfs and
|
||||
// interpreter_decrefs. Otherwise, increment increfs and decrefs.
|
||||
|
||||
#ifndef Py_CPYTHON_PYSTATS_H
|
||||
# error "this header file must not be included directly"
|
||||
#endif
|
||||
|
||||
#define SPECIALIZATION_FAILURE_KINDS 36
|
||||
|
||||
/* Stats for determining who is calling PyEval_EvalFrame */
|
||||
#define EVAL_CALL_TOTAL 0
|
||||
#define EVAL_CALL_VECTOR 1
|
||||
#define EVAL_CALL_GENERATOR 2
|
||||
#define EVAL_CALL_LEGACY 3
|
||||
#define EVAL_CALL_FUNCTION_VECTORCALL 4
|
||||
#define EVAL_CALL_BUILD_CLASS 5
|
||||
#define EVAL_CALL_SLOT 6
|
||||
#define EVAL_CALL_FUNCTION_EX 7
|
||||
#define EVAL_CALL_API 8
|
||||
#define EVAL_CALL_METHOD 9
|
||||
|
||||
#define EVAL_CALL_KINDS 10
|
||||
|
||||
typedef struct _specialization_stats {
|
||||
uint64_t success;
|
||||
uint64_t failure;
|
||||
uint64_t hit;
|
||||
uint64_t deferred;
|
||||
uint64_t miss;
|
||||
uint64_t deopt;
|
||||
uint64_t failure_kinds[SPECIALIZATION_FAILURE_KINDS];
|
||||
} SpecializationStats;
|
||||
|
||||
typedef struct _opcode_stats {
|
||||
SpecializationStats specialization;
|
||||
uint64_t execution_count;
|
||||
uint64_t pair_count[256];
|
||||
} OpcodeStats;
|
||||
|
||||
typedef struct _call_stats {
|
||||
uint64_t inlined_py_calls;
|
||||
uint64_t pyeval_calls;
|
||||
uint64_t frames_pushed;
|
||||
uint64_t frame_objects_created;
|
||||
uint64_t eval_calls[EVAL_CALL_KINDS];
|
||||
} CallStats;
|
||||
|
||||
typedef struct _object_stats {
|
||||
uint64_t increfs;
|
||||
uint64_t decrefs;
|
||||
uint64_t interpreter_increfs;
|
||||
uint64_t interpreter_decrefs;
|
||||
uint64_t allocations;
|
||||
uint64_t allocations512;
|
||||
uint64_t allocations4k;
|
||||
uint64_t allocations_big;
|
||||
uint64_t frees;
|
||||
uint64_t to_freelist;
|
||||
uint64_t from_freelist;
|
||||
uint64_t new_values;
|
||||
uint64_t dict_materialized_on_request;
|
||||
uint64_t dict_materialized_new_key;
|
||||
uint64_t dict_materialized_too_big;
|
||||
uint64_t dict_materialized_str_subclass;
|
||||
uint64_t dict_dematerialized;
|
||||
uint64_t type_cache_hits;
|
||||
uint64_t type_cache_misses;
|
||||
uint64_t type_cache_dunder_hits;
|
||||
uint64_t type_cache_dunder_misses;
|
||||
uint64_t type_cache_collisions;
|
||||
uint64_t optimization_attempts;
|
||||
uint64_t optimization_traces_created;
|
||||
uint64_t optimization_traces_executed;
|
||||
uint64_t optimization_uops_executed;
|
||||
/* Temporary value used during GC */
|
||||
uint64_t object_visits;
|
||||
} ObjectStats;
|
||||
|
||||
typedef struct _gc_stats {
|
||||
uint64_t collections;
|
||||
uint64_t object_visits;
|
||||
uint64_t objects_collected;
|
||||
} GCStats;
|
||||
|
||||
typedef struct _stats {
|
||||
OpcodeStats opcode_stats[256];
|
||||
CallStats call_stats;
|
||||
ObjectStats object_stats;
|
||||
GCStats *gc_stats;
|
||||
} PyStats;
|
||||
|
||||
|
||||
// Export for shared extensions like 'math'
|
||||
PyAPI_DATA(PyStats*) _Py_stats;
|
||||
|
||||
#ifdef _PY_INTERPRETER
|
||||
# define _Py_INCREF_STAT_INC() do { if (_Py_stats) _Py_stats->object_stats.interpreter_increfs++; } while (0)
|
||||
# define _Py_DECREF_STAT_INC() do { if (_Py_stats) _Py_stats->object_stats.interpreter_decrefs++; } while (0)
|
||||
#else
|
||||
# define _Py_INCREF_STAT_INC() do { if (_Py_stats) _Py_stats->object_stats.increfs++; } while (0)
|
||||
# define _Py_DECREF_STAT_INC() do { if (_Py_stats) _Py_stats->object_stats.decrefs++; } while (0)
|
||||
#endif
|
||||
|
|
@ -268,17 +268,17 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
|
|||
|
||||
#ifdef Py_STATS
|
||||
|
||||
#define STAT_INC(opname, name) do { if (_py_stats) _py_stats->opcode_stats[opname].specialization.name++; } while (0)
|
||||
#define STAT_DEC(opname, name) do { if (_py_stats) _py_stats->opcode_stats[opname].specialization.name--; } while (0)
|
||||
#define OPCODE_EXE_INC(opname) do { if (_py_stats) _py_stats->opcode_stats[opname].execution_count++; } while (0)
|
||||
#define CALL_STAT_INC(name) do { if (_py_stats) _py_stats->call_stats.name++; } while (0)
|
||||
#define OBJECT_STAT_INC(name) do { if (_py_stats) _py_stats->object_stats.name++; } while (0)
|
||||
#define STAT_INC(opname, name) do { if (_Py_stats) _Py_stats->opcode_stats[opname].specialization.name++; } while (0)
|
||||
#define STAT_DEC(opname, name) do { if (_Py_stats) _Py_stats->opcode_stats[opname].specialization.name--; } while (0)
|
||||
#define OPCODE_EXE_INC(opname) do { if (_Py_stats) _Py_stats->opcode_stats[opname].execution_count++; } while (0)
|
||||
#define CALL_STAT_INC(name) do { if (_Py_stats) _Py_stats->call_stats.name++; } while (0)
|
||||
#define OBJECT_STAT_INC(name) do { if (_Py_stats) _Py_stats->object_stats.name++; } while (0)
|
||||
#define OBJECT_STAT_INC_COND(name, cond) \
|
||||
do { if (_py_stats && cond) _py_stats->object_stats.name++; } while (0)
|
||||
#define EVAL_CALL_STAT_INC(name) do { if (_py_stats) _py_stats->call_stats.eval_calls[name]++; } while (0)
|
||||
do { if (_Py_stats && cond) _Py_stats->object_stats.name++; } while (0)
|
||||
#define EVAL_CALL_STAT_INC(name) do { if (_Py_stats) _Py_stats->call_stats.eval_calls[name]++; } while (0)
|
||||
#define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) \
|
||||
do { if (_py_stats && PyFunction_Check(callable)) _py_stats->call_stats.eval_calls[name]++; } while (0)
|
||||
#define GC_STAT_ADD(gen, name, n) do { if (_py_stats) _py_stats->gc_stats[(gen)].name += (n); } while (0)
|
||||
do { if (_Py_stats && PyFunction_Check(callable)) _Py_stats->call_stats.eval_calls[name]++; } while (0)
|
||||
#define GC_STAT_ADD(gen, name, n) do { if (_Py_stats) _Py_stats->gc_stats[(gen)].name += (n); } while (0)
|
||||
|
||||
// Export for '_opcode' shared extension
|
||||
PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
|
||||
|
|
|
|||
21
Include/internal/pycore_pystats.h
Normal file
21
Include/internal/pycore_pystats.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef Py_INTERNAL_PYSTATS_H
|
||||
#define Py_INTERNAL_PYSTATS_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef Py_BUILD_CORE
|
||||
# error "this header requires Py_BUILD_CORE define"
|
||||
#endif
|
||||
|
||||
#ifdef Py_STATS
|
||||
extern void _Py_StatsOn(void);
|
||||
extern void _Py_StatsOff(void);
|
||||
extern void _Py_StatsClear(void);
|
||||
extern int _Py_PrintSpecializationStats(int to_file);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // !Py_INTERNAL_PYSTATS_H
|
||||
|
|
@ -1,4 +1,9 @@
|
|||
|
||||
// Statistics on Python performance (public API).
|
||||
//
|
||||
// Define _Py_INCREF_STAT_INC() and _Py_DECREF_STAT_INC() used by Py_INCREF()
|
||||
// and Py_DECREF().
|
||||
//
|
||||
// See Include/cpython/pystats.h for the full API.
|
||||
|
||||
#ifndef Py_PYSTATS_H
|
||||
#define Py_PYSTATS_H
|
||||
|
|
@ -6,119 +11,16 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef Py_STATS
|
||||
|
||||
#define SPECIALIZATION_FAILURE_KINDS 36
|
||||
|
||||
/* Stats for determining who is calling PyEval_EvalFrame */
|
||||
#define EVAL_CALL_TOTAL 0
|
||||
#define EVAL_CALL_VECTOR 1
|
||||
#define EVAL_CALL_GENERATOR 2
|
||||
#define EVAL_CALL_LEGACY 3
|
||||
#define EVAL_CALL_FUNCTION_VECTORCALL 4
|
||||
#define EVAL_CALL_BUILD_CLASS 5
|
||||
#define EVAL_CALL_SLOT 6
|
||||
#define EVAL_CALL_FUNCTION_EX 7
|
||||
#define EVAL_CALL_API 8
|
||||
#define EVAL_CALL_METHOD 9
|
||||
|
||||
#define EVAL_CALL_KINDS 10
|
||||
|
||||
typedef struct _specialization_stats {
|
||||
uint64_t success;
|
||||
uint64_t failure;
|
||||
uint64_t hit;
|
||||
uint64_t deferred;
|
||||
uint64_t miss;
|
||||
uint64_t deopt;
|
||||
uint64_t failure_kinds[SPECIALIZATION_FAILURE_KINDS];
|
||||
} SpecializationStats;
|
||||
|
||||
typedef struct _opcode_stats {
|
||||
SpecializationStats specialization;
|
||||
uint64_t execution_count;
|
||||
uint64_t pair_count[256];
|
||||
} OpcodeStats;
|
||||
|
||||
typedef struct _call_stats {
|
||||
uint64_t inlined_py_calls;
|
||||
uint64_t pyeval_calls;
|
||||
uint64_t frames_pushed;
|
||||
uint64_t frame_objects_created;
|
||||
uint64_t eval_calls[EVAL_CALL_KINDS];
|
||||
} CallStats;
|
||||
|
||||
typedef struct _object_stats {
|
||||
uint64_t increfs;
|
||||
uint64_t decrefs;
|
||||
uint64_t interpreter_increfs;
|
||||
uint64_t interpreter_decrefs;
|
||||
uint64_t allocations;
|
||||
uint64_t allocations512;
|
||||
uint64_t allocations4k;
|
||||
uint64_t allocations_big;
|
||||
uint64_t frees;
|
||||
uint64_t to_freelist;
|
||||
uint64_t from_freelist;
|
||||
uint64_t new_values;
|
||||
uint64_t dict_materialized_on_request;
|
||||
uint64_t dict_materialized_new_key;
|
||||
uint64_t dict_materialized_too_big;
|
||||
uint64_t dict_materialized_str_subclass;
|
||||
uint64_t dict_dematerialized;
|
||||
uint64_t type_cache_hits;
|
||||
uint64_t type_cache_misses;
|
||||
uint64_t type_cache_dunder_hits;
|
||||
uint64_t type_cache_dunder_misses;
|
||||
uint64_t type_cache_collisions;
|
||||
uint64_t optimization_attempts;
|
||||
uint64_t optimization_traces_created;
|
||||
uint64_t optimization_traces_executed;
|
||||
uint64_t optimization_uops_executed;
|
||||
/* Temporary value used during GC */
|
||||
uint64_t object_visits;
|
||||
} ObjectStats;
|
||||
|
||||
typedef struct _gc_stats {
|
||||
uint64_t collections;
|
||||
uint64_t object_visits;
|
||||
uint64_t objects_collected;
|
||||
} GCStats;
|
||||
|
||||
typedef struct _stats {
|
||||
OpcodeStats opcode_stats[256];
|
||||
CallStats call_stats;
|
||||
ObjectStats object_stats;
|
||||
GCStats *gc_stats;
|
||||
} PyStats;
|
||||
|
||||
|
||||
PyAPI_DATA(PyStats) _py_stats_struct;
|
||||
PyAPI_DATA(PyStats *) _py_stats;
|
||||
|
||||
extern void _Py_StatsClear(void);
|
||||
extern void _Py_PrintSpecializationStats(int to_file);
|
||||
|
||||
#ifdef _PY_INTERPRETER
|
||||
|
||||
#define _Py_INCREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.interpreter_increfs++; } while (0)
|
||||
#define _Py_DECREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.interpreter_decrefs++; } while (0)
|
||||
|
||||
#if defined(Py_STATS) && !defined(Py_LIMITED_API)
|
||||
# define Py_CPYTHON_PYSTATS_H
|
||||
# include "cpython/pystats.h"
|
||||
# undef Py_CPYTHON_PYSTATS_H
|
||||
#else
|
||||
|
||||
#define _Py_INCREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.increfs++; } while (0)
|
||||
#define _Py_DECREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.decrefs++; } while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define _Py_INCREF_STAT_INC() ((void)0)
|
||||
#define _Py_DECREF_STAT_INC() ((void)0)
|
||||
|
||||
# define _Py_INCREF_STAT_INC() ((void)0)
|
||||
# define _Py_DECREF_STAT_INC() ((void)0)
|
||||
#endif // !Py_STATS
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !Py_PYSTATs_H */
|
||||
#endif // !Py_PYSTATS_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue