mirror of https://github.com/python/cpython
bpo-44725 : expose specialization stats in python (GH-27192)
This commit is contained in:
parent
6741794dd4
commit
ddd1c418c0
|
@ -321,6 +321,9 @@ typedef struct _stats {
|
|||
extern SpecializationStats _specialization_stats[256];
|
||||
#define STAT_INC(opname, name) _specialization_stats[opname].name++
|
||||
void _Py_PrintSpecializationStats(void);
|
||||
|
||||
PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
|
||||
|
||||
#else
|
||||
#define STAT_INC(opname, name) ((void)0)
|
||||
#endif
|
||||
|
|
|
@ -234,3 +234,13 @@ _specialized_instructions = [
|
|||
"LOAD_GLOBAL_MODULE",
|
||||
"LOAD_GLOBAL_BUILTIN",
|
||||
]
|
||||
|
||||
_specialization_stats = [
|
||||
"specialization_success",
|
||||
"specialization_failure",
|
||||
"hit",
|
||||
"deferred",
|
||||
"miss",
|
||||
"deopt",
|
||||
"unquickened",
|
||||
]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import dis
|
||||
from test.support.import_helper import import_module
|
||||
import unittest
|
||||
import opcode
|
||||
|
||||
_opcode = import_module("_opcode")
|
||||
from _opcode import stack_effect
|
||||
|
@ -64,5 +65,31 @@ class OpcodeTests(unittest.TestCase):
|
|||
self.assertEqual(nojump, common)
|
||||
|
||||
|
||||
class SpecializationStatsTests(unittest.TestCase):
|
||||
def test_specialization_stats(self):
|
||||
stat_names = opcode._specialization_stats
|
||||
|
||||
specialized_opcodes = [
|
||||
op[:-len("_ADAPTIVE")].lower() for
|
||||
op in opcode._specialized_instructions
|
||||
if op.endswith("_ADAPTIVE")]
|
||||
self.assertIn('load_attr', specialized_opcodes)
|
||||
self.assertIn('binary_subscr', specialized_opcodes)
|
||||
|
||||
stats = _opcode.get_specialization_stats()
|
||||
if stats is not None:
|
||||
self.assertIsInstance(stats, dict)
|
||||
self.assertCountEqual(stats.keys(), specialized_opcodes)
|
||||
self.assertCountEqual(
|
||||
stats['load_attr'].keys(),
|
||||
stat_names + ['fails'])
|
||||
for sn in stat_names:
|
||||
self.assertIsInstance(stats['load_attr'][sn], int)
|
||||
self.assertIsInstance(stats['load_attr']['fails'], dict)
|
||||
for k,v in stats['load_attr']['fails'].items():
|
||||
self.assertIsInstance(k, tuple)
|
||||
self.assertIsInstance(v, int)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Expose specialization stats in python via :func:`_opcode.get_specialization_stats`.
|
|
@ -1,5 +1,6 @@
|
|||
#include "Python.h"
|
||||
#include "opcode.h"
|
||||
#include "internal/pycore_code.h"
|
||||
|
||||
/*[clinic input]
|
||||
module _opcode
|
||||
|
@ -73,9 +74,28 @@ _opcode_stack_effect_impl(PyObject *module, int opcode, PyObject *oparg,
|
|||
return effect;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
||||
_opcode.get_specialization_stats
|
||||
|
||||
Return the specialization stats
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_opcode_get_specialization_stats_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=fcbc32fdfbec5c17 input=e1f60db68d8ce5f6]*/
|
||||
{
|
||||
#if SPECIALIZATION_STATS
|
||||
return _Py_GetSpecializationStats();
|
||||
#else
|
||||
Py_RETURN_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyMethodDef
|
||||
opcode_functions[] = {
|
||||
_OPCODE_STACK_EFFECT_METHODDEF
|
||||
_OPCODE_GET_SPECIALIZATION_STATS_METHODDEF
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -56,4 +56,22 @@ skip_optional_kwonly:
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=bcf66d25c2624197 input=a9049054013a1b77]*/
|
||||
|
||||
PyDoc_STRVAR(_opcode_get_specialization_stats__doc__,
|
||||
"get_specialization_stats($module, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return the specialization stats");
|
||||
|
||||
#define _OPCODE_GET_SPECIALIZATION_STATS_METHODDEF \
|
||||
{"get_specialization_stats", (PyCFunction)_opcode_get_specialization_stats, METH_NOARGS, _opcode_get_specialization_stats__doc__},
|
||||
|
||||
static PyObject *
|
||||
_opcode_get_specialization_stats_impl(PyObject *module);
|
||||
|
||||
static PyObject *
|
||||
_opcode_get_specialization_stats(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return _opcode_get_specialization_stats_impl(module);
|
||||
}
|
||||
/*[clinic end generated code: output=1699b4b1488b49c1 input=a9049054013a1b77]*/
|
||||
|
|
|
@ -40,6 +40,83 @@ Py_ssize_t _Py_QuickenedCount = 0;
|
|||
#if SPECIALIZATION_STATS
|
||||
SpecializationStats _specialization_stats[256] = { 0 };
|
||||
|
||||
#define ADD_STAT_TO_DICT(res, field) \
|
||||
do { \
|
||||
PyObject *val = PyLong_FromUnsignedLongLong(stats->field); \
|
||||
if (val == NULL) { \
|
||||
Py_DECREF(res); \
|
||||
return NULL; \
|
||||
} \
|
||||
if (PyDict_SetItemString(res, #field, val) == -1) { \
|
||||
Py_DECREF(res); \
|
||||
Py_DECREF(val); \
|
||||
return NULL; \
|
||||
} \
|
||||
Py_DECREF(val); \
|
||||
} while(0);
|
||||
|
||||
static PyObject*
|
||||
stats_to_dict(SpecializationStats *stats)
|
||||
{
|
||||
PyObject *res = PyDict_New();
|
||||
if (res == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ADD_STAT_TO_DICT(res, specialization_success);
|
||||
ADD_STAT_TO_DICT(res, specialization_failure);
|
||||
ADD_STAT_TO_DICT(res, hit);
|
||||
ADD_STAT_TO_DICT(res, deferred);
|
||||
ADD_STAT_TO_DICT(res, miss);
|
||||
ADD_STAT_TO_DICT(res, deopt);
|
||||
ADD_STAT_TO_DICT(res, unquickened);
|
||||
#if SPECIALIZATION_STATS_DETAILED
|
||||
if (stats->miss_types != NULL) {
|
||||
if (PyDict_SetItemString(res, "fails", stats->miss_types) == -1) {
|
||||
Py_DECREF(res);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
#undef ADD_STAT_TO_DICT
|
||||
|
||||
static int
|
||||
add_stat_dict(
|
||||
PyObject *res,
|
||||
int opcode,
|
||||
const char *name) {
|
||||
|
||||
SpecializationStats *stats = &_specialization_stats[opcode];
|
||||
PyObject *d = stats_to_dict(stats);
|
||||
if (d == NULL) {
|
||||
return -1;
|
||||
}
|
||||
int err = PyDict_SetItemString(res, name, d);
|
||||
Py_DECREF(d);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if SPECIALIZATION_STATS
|
||||
PyObject*
|
||||
_Py_GetSpecializationStats(void) {
|
||||
PyObject *stats = PyDict_New();
|
||||
if (stats == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int err = 0;
|
||||
err += add_stat_dict(stats, LOAD_ATTR, "load_attr");
|
||||
err += add_stat_dict(stats, LOAD_GLOBAL, "load_global");
|
||||
err += add_stat_dict(stats, BINARY_SUBSCR, "binary_subscr");
|
||||
if (err < 0) {
|
||||
Py_DECREF(stats);
|
||||
return NULL;
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define PRINT_STAT(name, field) fprintf(stderr, " %s." #field " : %" PRIu64 "\n", name, stats->field);
|
||||
|
||||
static void
|
||||
|
@ -71,6 +148,7 @@ print_stats(SpecializationStats *stats, const char *name)
|
|||
}
|
||||
#endif
|
||||
}
|
||||
#undef PRINT_STAT
|
||||
|
||||
void
|
||||
_Py_PrintSpecializationStats(void)
|
||||
|
|
Loading…
Reference in New Issue