Skip to content

bpo-46072: Add --with-pystats configure option to simplify gathering of VM stats #30116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 2 additions & 15 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 20

#if COLLECT_SPECIALIZATION_STATS

typedef struct _stats {
uint64_t specialization_success;
uint64_t specialization_failure;
Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add a --with-pystats configure option to turn on internal statistics
gathering.
2 changes: 1 addition & 1 deletion Modules/_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,8 @@ PyEval_InitThreads(void)
void
_PyEval_Fini(void)
{
#if PRINT_SPECIALIZATION_STATS
_Py_PrintSpecializationStats();
#ifdef Py_STATS
_Py_PrintSpecializationStats(1);
#endif
}

Expand Down
47 changes: 22 additions & 25 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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);
Expand All @@ -92,7 +91,6 @@ stats_to_dict(SpecializationStats *stats)
return NULL;
}
Py_DECREF(failure_kinds);
#endif
return res;
}
#undef ADD_STAT_TO_DICT
Expand All @@ -113,7 +111,7 @@ add_stat_dict(
return err;
}

#if COLLECT_SPECIALIZATION_STATS
#ifdef Py_STATS
PyObject*
_Py_GetSpecializationStats(void) {
PyObject *stats = PyDict_New();
Expand Down Expand Up @@ -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");
Expand All @@ -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]++

Expand Down Expand Up @@ -856,7 +853,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, S
}


#if COLLECT_SPECIALIZATION_STATS_DETAILED
#ifdef Py_STATS
static int
load_method_fail_kind(DesciptorClassification kind)
{
Expand Down Expand Up @@ -1082,7 +1079,7 @@ _Py_Specialize_LoadGlobal(
return 0;
}

#if COLLECT_SPECIALIZATION_STATS_DETAILED
#ifdef Py_STATS
static int
binary_subscr_fail_kind(PyTypeObject *container_type, PyObject *sub)
{
Expand Down Expand Up @@ -1306,7 +1303,7 @@ specialize_py_call(
return 0;
}

#if COLLECT_SPECIALIZATION_STATS_DETAILED
#ifdef Py_STATS
static int
builtin_call_fail_kind(int ml_flags)
{
Expand Down Expand Up @@ -1381,7 +1378,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)
{
Expand Down
2 changes: 1 addition & 1 deletion Tools/scripts/summarize_specialization_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,7 @@ enable_shared
enable_profiling
with_pydebug
with_trace_refs
enable_pystats
with_assertions
enable_optimizations
with_lto
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -6913,6 +6915,29 @@ then

$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.
Expand Down
17 changes: 16 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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], [
Expand Down
3 changes: 3 additions & 0 deletions pyconfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down