Skip to content

Commit

Permalink
Add option to write specialization stats to files and script to summa…
Browse files Browse the repository at this point in the history
…rize. (GH-27575)

* Add option to write stats to random file in a directory.

* Add script to summarize stats.
  • Loading branch information
markshannon committed Aug 4, 2021
1 parent ac811f9 commit c83919b
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 12 deletions.
1 change: 1 addition & 0 deletions Include/internal/pycore_code.h
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions Python/ceval.c
Expand Up @@ -4408,6 +4408,7 @@ opname ## _miss: \
cache_backoff(cache); \
} \
oparg = cache->original_oparg; \
STAT_DEC(opname, unquickened); \
JUMP_TO_INSTRUCTION(opname); \
}

Expand All @@ -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); \
}

Expand Down
43 changes: 31 additions & 12 deletions Python/specialize.c
Expand Up @@ -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);
Expand All @@ -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
}
Expand All @@ -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
Expand Down
41 changes: 41 additions & 0 deletions 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()

0 comments on commit c83919b

Please sign in to comment.