Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ddd1c41

Browse files
authored
bpo-44725 : expose specialization stats in python (GH-27192)
1 parent 6741794 commit ddd1c41

7 files changed

Lines changed: 158 additions & 1 deletion

File tree

Include/internal/pycore_code.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ typedef struct _stats {
321321
extern SpecializationStats _specialization_stats[256];
322322
#define STAT_INC(opname, name) _specialization_stats[opname].name++
323323
void _Py_PrintSpecializationStats(void);
324+
325+
PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
326+
324327
#else
325328
#define STAT_INC(opname, name) ((void)0)
326329
#endif

Lib/opcode.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,13 @@ def jabs_op(name, op):
234234
"LOAD_GLOBAL_MODULE",
235235
"LOAD_GLOBAL_BUILTIN",
236236
]
237+
238+
_specialization_stats = [
239+
"specialization_success",
240+
"specialization_failure",
241+
"hit",
242+
"deferred",
243+
"miss",
244+
"deopt",
245+
"unquickened",
246+
]

Lib/test/test__opcode.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import dis
22
from test.support.import_helper import import_module
33
import unittest
4+
import opcode
45

56
_opcode = import_module("_opcode")
67
from _opcode import stack_effect
@@ -64,5 +65,31 @@ def test_stack_effect_jump(self):
6465
self.assertEqual(nojump, common)
6566

6667

68+
class SpecializationStatsTests(unittest.TestCase):
69+
def test_specialization_stats(self):
70+
stat_names = opcode._specialization_stats
71+
72+
specialized_opcodes = [
73+
op[:-len("_ADAPTIVE")].lower() for
74+
op in opcode._specialized_instructions
75+
if op.endswith("_ADAPTIVE")]
76+
self.assertIn('load_attr', specialized_opcodes)
77+
self.assertIn('binary_subscr', specialized_opcodes)
78+
79+
stats = _opcode.get_specialization_stats()
80+
if stats is not None:
81+
self.assertIsInstance(stats, dict)
82+
self.assertCountEqual(stats.keys(), specialized_opcodes)
83+
self.assertCountEqual(
84+
stats['load_attr'].keys(),
85+
stat_names + ['fails'])
86+
for sn in stat_names:
87+
self.assertIsInstance(stats['load_attr'][sn], int)
88+
self.assertIsInstance(stats['load_attr']['fails'], dict)
89+
for k,v in stats['load_attr']['fails'].items():
90+
self.assertIsInstance(k, tuple)
91+
self.assertIsInstance(v, int)
92+
93+
6794
if __name__ == "__main__":
6895
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Expose specialization stats in python via :func:`_opcode.get_specialization_stats`.

Modules/_opcode.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Python.h"
22
#include "opcode.h"
3+
#include "internal/pycore_code.h"
34

45
/*[clinic input]
56
module _opcode
@@ -73,9 +74,28 @@ _opcode_stack_effect_impl(PyObject *module, int opcode, PyObject *oparg,
7374
return effect;
7475
}
7576

77+
/*[clinic input]
78+
79+
_opcode.get_specialization_stats
80+
81+
Return the specialization stats
82+
[clinic start generated code]*/
83+
84+
static PyObject *
85+
_opcode_get_specialization_stats_impl(PyObject *module)
86+
/*[clinic end generated code: output=fcbc32fdfbec5c17 input=e1f60db68d8ce5f6]*/
87+
{
88+
#if SPECIALIZATION_STATS
89+
return _Py_GetSpecializationStats();
90+
#else
91+
Py_RETURN_NONE;
92+
#endif
93+
}
94+
7695
static PyMethodDef
7796
opcode_functions[] = {
7897
_OPCODE_STACK_EFFECT_METHODDEF
98+
_OPCODE_GET_SPECIALIZATION_STATS_METHODDEF
7999
{NULL, NULL, 0, NULL}
80100
};
81101

Modules/clinic/_opcode.c.h

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,83 @@ Py_ssize_t _Py_QuickenedCount = 0;
4040
#if SPECIALIZATION_STATS
4141
SpecializationStats _specialization_stats[256] = { 0 };
4242

43+
#define ADD_STAT_TO_DICT(res, field) \
44+
do { \
45+
PyObject *val = PyLong_FromUnsignedLongLong(stats->field); \
46+
if (val == NULL) { \
47+
Py_DECREF(res); \
48+
return NULL; \
49+
} \
50+
if (PyDict_SetItemString(res, #field, val) == -1) { \
51+
Py_DECREF(res); \
52+
Py_DECREF(val); \
53+
return NULL; \
54+
} \
55+
Py_DECREF(val); \
56+
} while(0);
57+
58+
static PyObject*
59+
stats_to_dict(SpecializationStats *stats)
60+
{
61+
PyObject *res = PyDict_New();
62+
if (res == NULL) {
63+
return NULL;
64+
}
65+
ADD_STAT_TO_DICT(res, specialization_success);
66+
ADD_STAT_TO_DICT(res, specialization_failure);
67+
ADD_STAT_TO_DICT(res, hit);
68+
ADD_STAT_TO_DICT(res, deferred);
69+
ADD_STAT_TO_DICT(res, miss);
70+
ADD_STAT_TO_DICT(res, deopt);
71+
ADD_STAT_TO_DICT(res, unquickened);
72+
#if SPECIALIZATION_STATS_DETAILED
73+
if (stats->miss_types != NULL) {
74+
if (PyDict_SetItemString(res, "fails", stats->miss_types) == -1) {
75+
Py_DECREF(res);
76+
return NULL;
77+
}
78+
}
79+
#endif
80+
return res;
81+
}
82+
#undef ADD_STAT_TO_DICT
83+
84+
static int
85+
add_stat_dict(
86+
PyObject *res,
87+
int opcode,
88+
const char *name) {
89+
90+
SpecializationStats *stats = &_specialization_stats[opcode];
91+
PyObject *d = stats_to_dict(stats);
92+
if (d == NULL) {
93+
return -1;
94+
}
95+
int err = PyDict_SetItemString(res, name, d);
96+
Py_DECREF(d);
97+
return err;
98+
}
99+
100+
#if SPECIALIZATION_STATS
101+
PyObject*
102+
_Py_GetSpecializationStats(void) {
103+
PyObject *stats = PyDict_New();
104+
if (stats == NULL) {
105+
return NULL;
106+
}
107+
int err = 0;
108+
err += add_stat_dict(stats, LOAD_ATTR, "load_attr");
109+
err += add_stat_dict(stats, LOAD_GLOBAL, "load_global");
110+
err += add_stat_dict(stats, BINARY_SUBSCR, "binary_subscr");
111+
if (err < 0) {
112+
Py_DECREF(stats);
113+
return NULL;
114+
}
115+
return stats;
116+
}
117+
#endif
118+
119+
43120
#define PRINT_STAT(name, field) fprintf(stderr, " %s." #field " : %" PRIu64 "\n", name, stats->field);
44121

45122
static void
@@ -71,6 +148,7 @@ print_stats(SpecializationStats *stats, const char *name)
71148
}
72149
#endif
73150
}
151+
#undef PRINT_STAT
74152

75153
void
76154
_Py_PrintSpecializationStats(void)

0 commit comments

Comments
 (0)