summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2021-07-29 16:26:53 (GMT)
committerGitHub <noreply@github.com>2021-07-29 16:26:53 (GMT)
commitddd1c418c05da0de978c75dfb3e4a5b8d27e1d9f (patch)
treee43ba73ffc54a4e7a5d96c808592e43d40c45a5b
parent6741794dd420c6b9775a188690dbf265037cd69f (diff)
downloadcpython-ddd1c418c05da0de978c75dfb3e4a5b8d27e1d9f.zip
cpython-ddd1c418c05da0de978c75dfb3e4a5b8d27e1d9f.tar.gz
cpython-ddd1c418c05da0de978c75dfb3e4a5b8d27e1d9f.tar.bz2
bpo-44725 : expose specialization stats in python (GH-27192)
-rw-r--r--Include/internal/pycore_code.h3
-rw-r--r--Lib/opcode.py10
-rw-r--r--Lib/test/test__opcode.py27
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2021-07-23-15-17-01.bpo-44725.qcuKaa.rst1
-rw-r--r--Modules/_opcode.c20
-rw-r--r--Modules/clinic/_opcode.c.h20
-rw-r--r--Python/specialize.c78
7 files changed, 158 insertions, 1 deletions
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index bc46976..2cd7c70 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -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
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 7ba1519..7735848 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -234,3 +234,13 @@ _specialized_instructions = [
"LOAD_GLOBAL_MODULE",
"LOAD_GLOBAL_BUILTIN",
]
+
+_specialization_stats = [
+ "specialization_success",
+ "specialization_failure",
+ "hit",
+ "deferred",
+ "miss",
+ "deopt",
+ "unquickened",
+]
diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py
index 3bb64a7..928198a 100644
--- a/Lib/test/test__opcode.py
+++ b/Lib/test/test__opcode.py
@@ -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()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-07-23-15-17-01.bpo-44725.qcuKaa.rst b/Misc/NEWS.d/next/Core and Builtins/2021-07-23-15-17-01.bpo-44725.qcuKaa.rst
new file mode 100644
index 0000000..995cf14
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-07-23-15-17-01.bpo-44725.qcuKaa.rst
@@ -0,0 +1 @@
+Expose specialization stats in python via :func:`_opcode.get_specialization_stats`. \ No newline at end of file
diff --git a/Modules/_opcode.c b/Modules/_opcode.c
index 609a262..d440b5c 100644
--- a/Modules/_opcode.c
+++ b/Modules/_opcode.c
@@ -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}
};
diff --git a/Modules/clinic/_opcode.c.h b/Modules/clinic/_opcode.c.h
index 6915f21..6ef303b 100644
--- a/Modules/clinic/_opcode.c.h
+++ b/Modules/clinic/_opcode.c.h
@@ -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]*/
diff --git a/Python/specialize.c b/Python/specialize.c
index 5ebe596..f699065 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -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)