diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 2a50a0700fe..312939ee1d2 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -302,6 +302,7 @@ int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT #define SPECIALIZATION_STATS 0 #define SPECIALIZATION_STATS_DETAILED 0 +#define SPECIALIZATION_STATS_TO_FILE 0 #if SPECIALIZATION_STATS diff --git a/Python/ceval.c b/Python/ceval.c index 4f7edb84fc3..e3658b81388 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4408,6 +4408,7 @@ opname ## _miss: \ cache_backoff(cache); \ } \ oparg = cache->original_oparg; \ + STAT_DEC(opname, unquickened); \ JUMP_TO_INSTRUCTION(opname); \ } @@ -4423,6 +4424,7 @@ opname ## _miss: \ next_instr[-1] = _Py_MAKECODEUNIT(opname ## _ADAPTIVE, oparg); \ STAT_INC(opname, deopt); \ } \ + STAT_DEC(opname, unquickened); \ JUMP_TO_INSTRUCTION(opname); \ } diff --git a/Python/specialize.c b/Python/specialize.c index 680ffb428e4..1ca49d244a8 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -117,10 +117,10 @@ _Py_GetSpecializationStats(void) { #endif -#define PRINT_STAT(name, field) fprintf(stderr, " %s." #field " : %" PRIu64 "\n", name, stats->field); +#define PRINT_STAT(name, field) fprintf(out, " %s." #field " : %" PRIu64 "\n", name, stats->field); static void -print_stats(SpecializationStats *stats, const char *name) +print_stats(FILE *out, SpecializationStats *stats, const char *name) { PRINT_STAT(name, specialization_success); PRINT_STAT(name, specialization_failure); @@ -133,18 +133,18 @@ print_stats(SpecializationStats *stats, const char *name) if (stats->miss_types == NULL) { return; } - fprintf(stderr, " %s.fails:\n", name); + fprintf(out, " %s.fails:\n", name); PyObject *key, *count; Py_ssize_t pos = 0; while (PyDict_Next(stats->miss_types, &pos, &key, &count)) { PyObject *type = PyTuple_GetItem(key, 0); PyObject *name = PyTuple_GetItem(key, 1); PyObject *kind = PyTuple_GetItem(key, 2); - fprintf(stderr, " %s.", ((PyTypeObject *)type)->tp_name); - PyObject_Print(name, stderr, Py_PRINT_RAW); - fprintf(stderr, " ("); - PyObject_Print(kind, stderr, Py_PRINT_RAW); - fprintf(stderr, "): %ld\n", PyLong_AsLong(count)); + fprintf(out, " %s.", ((PyTypeObject *)type)->tp_name); + PyObject_Print(name, out, Py_PRINT_RAW); + fprintf(out, " ("); + PyObject_Print(kind, out, Py_PRINT_RAW); + fprintf(out, "): %ld\n", PyLong_AsLong(count)); } #endif } @@ -153,10 +153,29 @@ print_stats(SpecializationStats *stats, const char *name) void _Py_PrintSpecializationStats(void) { - printf("Specialization stats:\n"); - print_stats(&_specialization_stats[LOAD_ATTR], "load_attr"); - print_stats(&_specialization_stats[LOAD_GLOBAL], "load_global"); - print_stats(&_specialization_stats[BINARY_SUBSCR], "binary_subscr"); + FILE *out = stderr; +#if SPECIALIZATION_STATS_TO_FILE + /* Write to a file instead of stderr. */ +# ifdef MS_WINDOWS + const char *dirname = "c:\\temp\\py_stats\\"; +# else + 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 + print_stats(out, &_specialization_stats[LOAD_ATTR], "load_attr"); + print_stats(out, &_specialization_stats[LOAD_GLOBAL], "load_global"); + print_stats(out, &_specialization_stats[BINARY_SUBSCR], "binary_subscr"); + if (out != stderr) { + fclose(out); + } } #if SPECIALIZATION_STATS_DETAILED diff --git a/Tools/scripts/summarize_specialization_stats.py b/Tools/scripts/summarize_specialization_stats.py new file mode 100644 index 00000000000..1a13ae8cb6c --- /dev/null +++ b/Tools/scripts/summarize_specialization_stats.py @@ -0,0 +1,41 @@ +"""Print a summary of specialization stats for all files in the +default stats folders. +""" + +import collections +import os.path + +if os.name == "nt": + DEFAULT_DIR = "c:\\temp\\py_stats\\" +else: + DEFAULT_DIR = "/tmp/py_stats/" + + +TOTAL = "deferred", "hit", "miss", "unquickened" + +def print_stats(name, family_stats): + total = sum(family_stats[kind] for kind in TOTAL) + if total == 0: + return + print(name+":") + for key in sorted(family_stats): + if not key.startswith("specialization"): + print(f"{key:>12}:{family_stats[key]:>12} {100*family_stats[key]/total:0.1f}%") + for key in ("specialization_success", "specialization_failure"): + print(f" {key}:{family_stats[key]:>12}") + +def main(): + stats = collections.defaultdict(collections.Counter) + for filename in os.listdir(DEFAULT_DIR): + for line in open(os.path.join(DEFAULT_DIR, filename)): + key, value = line.split(":") + key = key.strip() + family, stat = key.split(".") + value = int(value.strip()) + stats[family][stat] += value + + for name in sorted(stats): + print_stats(name, stats[name]) + +if __name__ == "__main__": + main()