From 342b93f9f28746abb7b221a61d5a9b26ccbb395a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 15 Dec 2021 15:32:32 +0000 Subject: bpo-46072: Add --with-pystats configure option to simplify gathering of VM stats (GH-30116) * Simplify specialization stats collection macros. * Add --enable-pystats option to configure. * Update specialization summary script to handle larger number of kinds --- Include/internal/pycore_code.h | 17 +------- .../Build/2021-12-15-10-37-44.bpo-46072.GgeAU3.rst | 2 + Modules/_opcode.c | 2 +- Python/ceval.c | 4 +- Python/specialize.c | 47 ++++++++++------------ Tools/scripts/summarize_specialization_stats.py | 2 +- configure | 25 ++++++++++++ configure.ac | 17 +++++++- pyconfig.h.in | 3 ++ 9 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2021-12-15-10-37-44.bpo-46072.GgeAU3.rst diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index b0463e3..e9b1ad4 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -276,22 +276,11 @@ void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache); void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache); -#define PRINT_SPECIALIZATION_STATS 0 -#define PRINT_SPECIALIZATION_STATS_DETAILED 0 -#define PRINT_SPECIALIZATION_STATS_TO_FILE 0 -#ifdef Py_DEBUG -#define COLLECT_SPECIALIZATION_STATS 1 -#define COLLECT_SPECIALIZATION_STATS_DETAILED 1 -#else -#define COLLECT_SPECIALIZATION_STATS PRINT_SPECIALIZATION_STATS -#define COLLECT_SPECIALIZATION_STATS_DETAILED PRINT_SPECIALIZATION_STATS_DETAILED -#endif +#ifdef Py_STATS #define SPECIALIZATION_FAILURE_KINDS 30 -#if COLLECT_SPECIALIZATION_STATS - typedef struct _stats { uint64_t specialization_success; uint64_t specialization_failure; @@ -300,15 +289,13 @@ typedef struct _stats { uint64_t miss; uint64_t deopt; uint64_t unquickened; -#if COLLECT_SPECIALIZATION_STATS_DETAILED uint64_t specialization_failure_kinds[SPECIALIZATION_FAILURE_KINDS]; -#endif } SpecializationStats; extern SpecializationStats _specialization_stats[256]; #define STAT_INC(opname, name) _specialization_stats[opname].name++ #define STAT_DEC(opname, name) _specialization_stats[opname].name-- -void _Py_PrintSpecializationStats(void); +void _Py_PrintSpecializationStats(int to_file); PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void); diff --git a/Misc/NEWS.d/next/Build/2021-12-15-10-37-44.bpo-46072.GgeAU3.rst b/Misc/NEWS.d/next/Build/2021-12-15-10-37-44.bpo-46072.GgeAU3.rst new file mode 100644 index 0000000..9cc8b6c --- /dev/null +++ b/Misc/NEWS.d/next/Build/2021-12-15-10-37-44.bpo-46072.GgeAU3.rst @@ -0,0 +1,2 @@ +Add a --with-pystats configure option to turn on internal statistics +gathering. diff --git a/Modules/_opcode.c b/Modules/_opcode.c index 39e3066..4812716 100644 --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -85,7 +85,7 @@ static PyObject * _opcode_get_specialization_stats_impl(PyObject *module) /*[clinic end generated code: output=fcbc32fdfbec5c17 input=e1f60db68d8ce5f6]*/ { -#if COLLECT_SPECIALIZATION_STATS +#ifdef Py_STATS return _Py_GetSpecializationStats(); #else Py_RETURN_NONE; diff --git a/Python/ceval.c b/Python/ceval.c index b9444b2..87d6a22 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -349,8 +349,8 @@ PyEval_InitThreads(void) void _PyEval_Fini(void) { -#if PRINT_SPECIALIZATION_STATS - _Py_PrintSpecializationStats(); +#ifdef Py_STATS + _Py_PrintSpecializationStats(1); #endif } diff --git a/Python/specialize.c b/Python/specialize.c index 5cf327d..7d4387b 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -39,7 +39,7 @@ */ Py_ssize_t _Py_QuickenedCount = 0; -#if COLLECT_SPECIALIZATION_STATS +#ifdef Py_STATS SpecializationStats _specialization_stats[256] = { 0 }; #define ADD_STAT_TO_DICT(res, field) \ @@ -71,7 +71,6 @@ stats_to_dict(SpecializationStats *stats) ADD_STAT_TO_DICT(res, miss); ADD_STAT_TO_DICT(res, deopt); ADD_STAT_TO_DICT(res, unquickened); -#if COLLECT_SPECIALIZATION_STATS_DETAILED PyObject *failure_kinds = PyTuple_New(SPECIALIZATION_FAILURE_KINDS); if (failure_kinds == NULL) { Py_DECREF(res); @@ -92,7 +91,6 @@ stats_to_dict(SpecializationStats *stats) return NULL; } Py_DECREF(failure_kinds); -#endif return res; } #undef ADD_STAT_TO_DICT @@ -113,7 +111,7 @@ add_stat_dict( return err; } -#if COLLECT_SPECIALIZATION_STATS +#ifdef Py_STATS PyObject* _Py_GetSpecializationStats(void) { PyObject *stats = PyDict_New(); @@ -151,35 +149,34 @@ print_stats(FILE *out, SpecializationStats *stats, const char *name) PRINT_STAT(name, miss); PRINT_STAT(name, deopt); PRINT_STAT(name, unquickened); -#if PRINT_SPECIALIZATION_STATS_DETAILED for (int i = 0; i < SPECIALIZATION_FAILURE_KINDS; i++) { fprintf(out, " %s.specialization_failure_kinds[%d] : %" PRIu64 "\n", name, i, stats->specialization_failure_kinds[i]); } -#endif } #undef PRINT_STAT void -_Py_PrintSpecializationStats(void) +_Py_PrintSpecializationStats(int to_file) { FILE *out = stderr; -#if PRINT_SPECIALIZATION_STATS_TO_FILE - /* Write to a file instead of stderr. */ + if (to_file) { + /* Write to a file instead of stderr. */ # ifdef MS_WINDOWS - const char *dirname = "c:\\temp\\py_stats\\"; + const char *dirname = "c:\\temp\\py_stats\\"; # else - const char *dirname = "/tmp/py_stats/"; + const char *dirname = "/tmp/py_stats/"; # endif - char buf[48]; - sprintf(buf, "%s%u_%u.txt", dirname, (unsigned)clock(), (unsigned)rand()); - FILE *fout = fopen(buf, "w"); - if (fout) { - out = fout; - } -#else - fprintf(out, "Specialization stats:\n"); -#endif + char buf[48]; + sprintf(buf, "%s%u_%u.txt", dirname, (unsigned)clock(), (unsigned)rand()); + FILE *fout = fopen(buf, "w"); + if (fout) { + out = fout; + } + } + else { + fprintf(out, "Specialization stats:\n"); + } print_stats(out, &_specialization_stats[LOAD_ATTR], "load_attr"); print_stats(out, &_specialization_stats[LOAD_GLOBAL], "load_global"); print_stats(out, &_specialization_stats[LOAD_METHOD], "load_method"); @@ -194,7 +191,7 @@ _Py_PrintSpecializationStats(void) } } -#if COLLECT_SPECIALIZATION_STATS_DETAILED +#ifdef Py_STATS #define SPECIALIZATION_FAIL(opcode, kind) _specialization_stats[opcode].specialization_failure_kinds[kind]++ @@ -860,7 +857,7 @@ success: } -#if COLLECT_SPECIALIZATION_STATS_DETAILED +#ifdef Py_STATS static int load_method_fail_kind(DesciptorClassification kind) { @@ -1086,7 +1083,7 @@ success: return 0; } -#if COLLECT_SPECIALIZATION_STATS_DETAILED +#ifdef Py_STATS static int binary_subscr_fail_kind(PyTypeObject *container_type, PyObject *sub) { @@ -1380,7 +1377,7 @@ specialize_py_call( return 0; } -#if COLLECT_SPECIALIZATION_STATS_DETAILED +#ifdef Py_STATS static int builtin_call_fail_kind(int ml_flags) { @@ -1459,7 +1456,7 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, } } -#if COLLECT_SPECIALIZATION_STATS_DETAILED +#ifdef Py_STATS static int call_fail_kind(PyObject *callable) { diff --git a/Tools/scripts/summarize_specialization_stats.py b/Tools/scripts/summarize_specialization_stats.py index cc3ef85..15b1887 100644 --- a/Tools/scripts/summarize_specialization_stats.py +++ b/Tools/scripts/summarize_specialization_stats.py @@ -24,7 +24,7 @@ def print_stats(name, family_stats): for key in ("specialization_success", "specialization_failure"): print(f" {key}:{family_stats[key]:>12}") total_failures = family_stats["specialization_failure"] - failure_kinds = [ 0 ] * 20 + failure_kinds = [ 0 ] * 30 for key in family_stats: if not key.startswith("specialization_failure_kind"): continue diff --git a/configure b/configure index 583e7d1..1ede299 100755 --- a/configure +++ b/configure @@ -1006,6 +1006,7 @@ enable_shared enable_profiling with_pydebug with_trace_refs +enable_pystats with_assertions enable_optimizations with_lto @@ -1713,6 +1714,7 @@ Optional Features: no) --enable-profiling enable C-level code profiling with gprof (default is no) + --enable-pystats enable internal statistics gathering (default is no) --enable-optimizations enable expensive, stable optimizations (PGO, etc.) (default is no) --enable-loadable-sqlite-extensions @@ -6915,6 +6917,29 @@ $as_echo "#define Py_TRACE_REFS 1" >>confdefs.h fi + +# Check for --enable-pystats +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-pystats" >&5 +$as_echo_n "checking for --enable-pystats... " >&6; } +# Check whether --enable-pystats was given. +if test "${enable_pystats+set}" = set; then : + enableval=$enable_pystats; +else + enable_pystats=no + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_pystats" >&5 +$as_echo "$enable_pystats" >&6; } + +if test "x$enable_pystats" = xyes; then : + + +$as_echo "#define Py_STATS 1" >>confdefs.h + + +fi + # Check for --with-assertions. # This allows enabling assertions without Py_DEBUG. assertions='false' diff --git a/configure.ac b/configure.ac index 5256a61..86404bc 100644 --- a/configure.ac +++ b/configure.ac @@ -1387,6 +1387,21 @@ then AC_DEFINE(Py_TRACE_REFS, 1, [Define if you want to enable tracing references for debugging purpose]) fi + +# Check for --enable-pystats +AC_MSG_CHECKING([for --enable-pystats]) +AC_ARG_ENABLE([pystats], + [AS_HELP_STRING( + [--enable-pystats], + [enable internal statistics gathering (default is no)])],, + [enable_pystats=no] +) +AC_MSG_RESULT([$enable_pystats]) + +AS_VAR_IF([enable_pystats], [yes], [ + AC_DEFINE([Py_STATS], [1], [Define if you want to enable internal statistics gathering.]) +]) + # Check for --with-assertions. # This allows enabling assertions without Py_DEBUG. assertions='false' @@ -6323,7 +6338,7 @@ AC_DEFUN([PY_STDLIB_MOD], [ ]) dnl Define simple stdlib extension module -dnl Always enable unless the module is listed in py_stdlib_not_available +dnl Always enable unless the module is listed in py_stdlib_not_available dnl PY_STDLIB_MOD_SIMPLE([NAME], [CFLAGS], [LDFLAGS]) dnl cflags and ldflags are optional AC_DEFUN([PY_STDLIB_MOD_SIMPLE], [ diff --git a/pyconfig.h.in b/pyconfig.h.in index efad243..e6e8165 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1496,6 +1496,9 @@ SipHash13: 3, externally defined: 0 */ #undef Py_HASH_ALGORITHM +/* Define if you want to enable internal statistics gathering. */ +#undef Py_STATS + /* Define if you want to enable tracing references for debugging purpose */ #undef Py_TRACE_REFS -- cgit v0.12